Welcome to Abdul Malik Ikhsan's Blog

Using laminas-cli to Consume Symfony Console Command in Mezzio Application

Posted in Laminas, Mezzio, Symfony 4 by samsonasik on April 17, 2020


So, you want to use Symfony Console Command in Mezzio Application? You can! There is laminas-cli for that. While it still in development, you already can give it a try. First, I assume that you already installed the mezzio application. Next, you can set minimum-stability and prefer-stable config in your composer.json:

➜  composer config minimum-stability dev
➜  composer config prefer-stable true

By above command, you can ensure that you can install the non-stable dependency, while prefer stable version if found. Next, you can require the laminas-cli via command:

➜  composer require laminas/laminas-cli

After installed, let’s create our first command: “HelloWorld command”, like the following:

namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use function sprintf;

final class HelloWorld extends Command
{
    protected function configure()
    {
        $this
            ->addArgument('message', InputArgument::REQUIRED, 'Greeting Message');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $message = $input->getArgument('message');
        $output->writeln(sprintf('<info>Hello to world: %s<info>! ', $message));

        return 0;
    }
}

Greet! Now, time to register it to our App\ConfigProvider class:

<?php

declare(strict_types=1);

namespace App;

class ConfigProvider
{
    public function __invoke(): array
    {
        return [
            // ...
            'laminas-cli'  => $this->getCliConfig(),
            // ...
        ];
    }

    public function getCliConfig(): array
    {
        return [
            'commands' => [
                // ...
                'app:hello-world' => Command\HelloWorld::class,
                // ...
            ],
        ];
    }

    public function getDependencies(): array
    {
        return [
            'invokables'         => [
                // ...
                Command\HelloWorld::class  => Command\HelloWorld::class,
                // ...
            ],
        ];
    }

    // ...
}

First, in getDependencies(), we register the command, if the command has dependency, you need to provide factory for it. And then, in __invoke() method, we register the commands, which you can move the commands definition in separate method. That’s it! Now, you can run the command:

➜  vendor/bin/laminas app:hello-world "Good Morning"
Hello to world: Good Morning! 

Functional Test Symfony 4 with Kahlan 4

Posted in Symfony 4, testing by samsonasik on January 3, 2018

Yes, there is a bundle for it, but currently not fully work well with kahlan 4 yet. However, we can still use kahlan 4 for it. The simplest way is define Symfony 4 skeleton bootstrap in kahlan config, and use its property at specs, for example, we configure config at kahlan-config.php as follows:

<?php // kahlan-config.php 

use App\Kernel;
use Kahlan\Filter\Filters;
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;

Filters::apply($this, 'bootstrap', function($next) {

    require __DIR__.'/config/bootstrap.php';

    umask(0000);
    Debug::enable();

    $root = $this->suite()->root();
    $root->beforeAll(function () {
        $this->request = Request::createFromGlobals();
        $this->kernel  = new Kernel('test', false);
    });

    return $next();

});

Above settings are minimal, if you need more setup, you can define there. If you didn’t require kahlan/kahlan:^4.0, you can require via composer:

$ composer require --dev kahlan/kahlan:^4.0

Give a try

Let’s try testing a famous /lucky/number from LuckyController. We have the following controller:

<?php // src/Controller/LuckyController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class LuckyController extends Controller
{
    /**
     * @Route("/lucky/number", name="lucky_number")
     */
    public function number()
    {
        $number = mt_rand(0, 100);

        return $this->render('lucky/number.html.twig', [
            'number' => $number,
        ]);
    }
}

And our twig file is:

{# templates/lucky/number.html.twig #}

<h1>Your lucky number is {{ number }}</h1>

We can place test under spec directory at root directory, for its test, we can create a spec/Controller directory:

kahlan.config.php
├── spec
│   └── Controller

Now, we can create the test as follows with make request to the ‘/lucky/number’ page and get its response. We can use toMatchEcho matcher provided with regex to get match random number of mt_rand(0, 100) that printed inside a response html content:

<?php // spec/Controller/LuckyControllerSpec.php

namespace App\Spec\Controller;

describe('LuckyController', function () {

    describe('/lucky/number', function () {

        it('shows lucky number', function () {

            $request = $this->request->create('/lucky/number', 'GET');
            $response = $this->kernel->handle($request);

            expect(function () use ($response) {
                $response->send();
            })->toMatchEcho(
                "#Your lucky number is ([0-9]|[1-8][0-9]|9[0-9]|100)#"
            );

        });

    });

});

Time to run it with command:

$ vendor/bin/kahlan 

We will get the success output:

That’s it 😉

Tagged with: