3
\$\begingroup\$

I am working on a project that will allow developers to create a console application using a dependency injection service container. Both the console and container are Symfony components with some glue code to make it all work together.

The project can be found here and the script in question is here.

I'm hoping that some people with more experience using either component could chime in on anything I am doing wrong, or mention things you would like to see supported in case it could be useful to you.

<?php

namespace KHerGe\Shell;

use KHerGe\Shell\DependencyInjection\CommandCompilerPass;
use KHerGe\Shell\DependencyInjection\EventDispatcherCompilerPass;
use KHerGe\Shell\DependencyInjection\HelperCompilerPass;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Manages the assembly of a console application using a service container.
 *
 * @author Kevin Herrera <[email protected]>
 */
class Application
{
    /**
     * The service container.
     *
     * @var ContainerBuilder
     */
    private $container;

    /**
     * The compiled state of the service container.
     *
     * @var boolean
     */
    private $compiled = false;

    /**
     * The list of default helpers.
     *
     * @var array
     */
    private static $helpers = array(
        'shell.console.helper.descriptor' => 'Symfony\Component\Console\Helper\DescriptorHelper',
        'shell.console.helper.dialog' => 'Symfony\Component\Console\Helper\DialogHelper',
        'shell.console.helper.formatter' => 'Symfony\Component\Console\Helper\FormatterHelper',
        'shell.console.helper.progress' => 'Symfony\Component\Console\Helper\ProgressHelper',
        'shell.console.helper.table' => 'Symfony\Component\Console\Helper\TableHelper',
    );

    /**
     * Sets the service container.
     *
     * @param ContainerBuilder $container The service container.
     */
    public function __construct(ContainerBuilder $container = null)
    {
        if (null === $container) {
            $container = new ContainerBuilder();
        }

        $this->buildContainer($container);

        $this->container = $container;
    }

    /**
     * Creates a new Shell application.
     *
     * @param ContainerBuilder $container The service container.
     *
     * @return Application The new Shell application.
     */
    public static function create(ContainerBuilder $container = null)
    {
        return new static($container);
    }

    /**
     * Returns the service container.
     *
     * @return ContainerBuilder The service container.
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * Loads a service container configuration file.
     *
     * @param string $file The path to the configuration file.
     *
     * @return Application The same Shell application.
     */
    public function load($file)
    {
        $locator = new FileLocator(dirname($file));
        $loaders = array(
            new IniFileLoader($this->container, $locator),
            new PhpFileLoader($this->container, $locator),
        );

        if (class_exists('DOMDocument')) {
            $loaders[] = new XmlFileLoader($this->container, $locator);
        }

        if (class_exists('Symfony\Component\Yaml\Yaml')) {
            $loaders[] = new YamlFileLoader($this->container, $locator);
        }

        $loader = new DelegatingLoader(new LoaderResolver($loaders));
        $loader->load(basename($file));

        return $this;
    }

    /**
     * Runs the Shell console application.
     *
     * @return integer The command exit status.
     */
    public function run()
    {
        if (false === $this->compiled) {
            $this->compiled = true;

            $this->container->compile();
        }

        return $this->container->get('shell.console')->run(
            $this->container->get('shell.console.input'),
            $this->container->get('shell.console.output')
        );
    }

    /**
     * Builds the container with default services.
     *
     * @param ContainerBuilder $container The service container.
     */
    private function buildContainer(ContainerBuilder $container)
    {
        if (!$container->has('shell.container')) {
            $container->set('shell.container', $container);
        }

        $container->addCompilerPass(new CommandCompilerPass());
        $container->addCompilerPass(new EventDispatcherCompilerPass());
        $container->addCompilerPass(new HelperCompilerPass());

        $this->setConsole($container);
        $this->setEventDispatcher($container);
        $this->setHelperSet($container);
        $this->setInput($container);
        $this->setOutput($container);
    }

    /**
     * Creates a console definition, if one has not already been set.
     *
     * @param ContainerBuilder $container The container builder.
     */
    private function setConsole(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('shell.console')) {
            $definition = new Definition('Symfony\Component\Console\Application');
            $definition->addArgument('%app.name%');
            $definition->addArgument('%app.version%');

            $container->setDefinition('shell.console', $definition);
        }
    }

    /**
     * Creates an event dispatcher definition, if one has not already been set.
     *
     * @param ContainerBuilder $container The container builder.
     */
    private function setEventDispatcher(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('shell.console.event_dispatcher')) {
            $container->setDefinition(
                'shell.console.event_dispatcher',
                new Definition('Symfony\Component\EventDispatcher\EventDispatcher')
            );

            $container->getDefinition('shell.console')->addMethodCall(
                'setDispatcher',
                array(new Reference('shell.console.event_dispatcher'))
            );
        }
    }

    /**
     * Creates a helper set, if one has not already been set.
     *
     * @param ContainerBuilder $container The container builder.
     */
    private function setHelperSet(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('shell.console.helper_set')) {
            $container->setDefinition(
                'shell.console.helper_set',
                new Definition('Symfony\Component\Console\Helper\HelperSet')
            );
        }

        foreach (self::$helpers as $id => $class) {
            if (class_exists($class)) {
                if (!$container->hasDefinition($id)) {
                    $definition = new Definition($class);
                    $definition->addTag('shell.console.helper');
                    $container->setDefinition($id, $definition);
                }
            }
        }

        $container->getDefinition('shell.console')->addMethodCall(
            'setHelperSet',
            array(new Reference('shell.console.helper_set'))
        );
    }

    /**
     * Creates a console input definition, if one has not already been set.
     *
     * @param ContainerBuilder $container The container builder.
     */
    private function setInput(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('shell.console.input')) {
            $container->setDefinition(
                'shell.console.input',
                new Definition('Symfony\Component\Console\Input\ArgvInput')
            );
        }
    }

    /**
     * Creates a console output definition, if one has not already been set.
     *
     * @param ContainerBuilder $container The container builder.
     */
    private function setOutput(ContainerBuilder $container)
    {
        if (!$container->hasDefinition('shell.console.output')) {
            $container->setDefinition(
                'shell.console.output',
                new Definition('Symfony\Component\Console\Output\ConsoleOutput')
            );
        }
    }
}
\$\endgroup\$
0

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.