Add CLI Tool into PHP-DI ContainerBuilder (Slim 4)

Hey guys,

i’m new to slim 4 and i want to add symfony/console to my slim 4 application.

I use this repo to implement the CLI: GitHub - slimphp/Slim-Skeleton: Slim Framework 4 Skeleton Application

First i install symfony/console
composer require symfony/console

In my app folder i create the an “cli.php” file:

<?php

use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputOption;

return function (ContainerBuilder $containerBuilder) {
$containerBuilder->addDefinitions([
    Application::class => function (ContainerInterface $c) {
        $application = new Application();

        $application->getDefinition()->addOption(
            new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', 'development')
        );

        foreach ($c->get('settings')['commands'] as $class) {
            $application->add($c->get($class));
        }

        return $application;
    },
]);
};

In my index.php (located in public folder):

// Set up command line
$cli = require __DIR__ . '/../app/cli.php';
$cli($containerBuilder);

I created an example command in src/Command/ExampleCommand.php:

<?php

namespace App\Console;

use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * Command.
 */
final class ExampleCommand extends Command
{
    /**
     * @var ContainerInterface
     */
    private $container;

    /**
     * Constructor.
     *
     * @param ContainerInterface $container The container
     * @param string|null $name The name
     */
    public function __construct(ContainerInterface $container, ?string $name = null)
    {
        parent::__construct($name);
        $this->container = $container;
    }

    /**
     * Configure.
     *
     * @return void
     */
    protected function configure(): void
    {
        parent::configure();

        $this->setName('example');
        $this->setDescription('A sample command');
    }

    /**
     * Execute command.
     *
     * @param InputInterface $input The input
     * @param OutputInterface $output The output
     *
     * @return int The error code, 0 on success
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln(sprintf('<info>Hello, console</info>'));

        return 0;
    }
}

When i try to run php slim/app/cli.php nothing happens. I’m not sure if cli is added in the common way.

I very thankful for help!

It looks like the cli.php script just returns nothing, so nothing should happen.

Have you tried to fetch the Console Application instance from the container instead?

I’m not sure what to do?

I create an example repo:
https://bitbucket.org/aboutsam/4slim/commits/

In this commit, i added the cli.php file and add the ExampleClass into my settings.php.

How do i fetch the console application? I’m very new to this

In this commit, i create the basic symfony php file.

when i run this command:
php bin/console.php list

I see my created ExampleCommand but in this file i cannot create a new item for my database because he don’t know my other classes in the PHP-DI wrapper.

I’m sure it must be a copy of this example script.
You may also find a working example there:

If i use your example of php/console.php i receive the following error:

PHP Warning:  require(/Users/sam/Workspace/lemabo/slim/bin/../config/bootstrap.php): failed to open stream: No such file or directory in /Users/sam/Workspace/lemabo/slim/bin/console.php on line 30
PHP Stack trace:
PHP   1. {main}() /Users/sam/Workspace/lemabo/slim/bin/console.php:0

I’m not sure how to implement your code into my working slim 4 skeleton. can you help?

The question is why don’t you just use the (working) Slim 4 Skeleton project and adapt it to your needs?

i took over the project.

currently, i can now access my database via command line.

i try your advise. i store my public/index.php file into an variable and load my Application via

$application = $container->get(Application:class);
$application->run();

My command is now accessable in the docker container.

To solve my problem, i create a copy of public/index.php and place into my bin folder… my $application now load the app from this file.

so only this i have to figure out is to load the CommandClasses in my ContainerBuilder. :slight_smile:

i create a copy of public/index.php and place into my bin folder

This makes no sense and could be a security issue. The index.php is only for the web access and not for the console. For the console there is already a file called: bin/console.php.

my console.php

<?php

require __DIR__ . '/../vendor/autoload.php';

use App\Command\ExampleCommand;
use Symfony\Component\Console\Application;

$application = new Application();

$application->add(new ExampleCommand());

$application->run();

my app.php

<?php

declare(strict_types=1);

use DI\ContainerBuilder;

use Slim\Factory\AppFactory;

use Dotenv\Dotenv;

require __DIR__ . '/../vendor/autoload.php';

$dotenv = Dotenv::createImmutable(__DIR__, '/../.env');

$dotenv->load();

// Instantiate PHP-DI ContainerBuilder

$containerBuilder = new ContainerBuilder();

if (false) { // Should be set to true in production

$containerBuilder->enableCompilation(__DIR__ . '/../var/cache');

}

// Set up settings

$settings = require __DIR__ . '/../app/settings.php';

$settings($containerBuilder);

// Set up dependencies

$dependencies = require __DIR__ . '/../app/dependencies.php';

$dependencies($containerBuilder);

// Set up repositories

$repositories = require __DIR__ . '/../app/repositories.php';

$repositories($containerBuilder);

// Build PHP-DI Container instance

$container = $containerBuilder->build();

// Instantiate the app

AppFactory::setContainer($container);

$app = AppFactory::create();

$callableResolver = $app->getCallableResolver();

// Register middleware

$middleware = require __DIR__ . '/../app/middleware.php';

$middleware($app);

// Register routes

$routes = require __DIR__ . '/../app/routes.php';

$routes($app);

// add bin/console.php

$commands = require __DIR__ . '/../bin/console.php';

$commands($app);

return $app;