Skip to content
This repository has been archived by the owner on Feb 1, 2020. It is now read-only.
/ console Public archive

A console application assembled through dependency injection.

Notifications You must be signed in to change notification settings

kherge-archive/console

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

86 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Latest Stable Version Latest Unstable Version Total Downloads

Console

composer require box-project/console

Console simplifies the process of building a command line application using the dependency injection design pattern. Input and output management is already handled for you. You simply need to create your commands and register each as a service.

use Box\Component\Console\Application;
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$application = new Application($container);

$container->compile()
$application->run();

Requirements

  • kherge/file ~1.3
  • herrera-io/object-storage ~1.0
  • symfony/config ~2.5
  • symfony/console ~2.5
  • symfony/dependency-injection ~2.5
  • symfony/yaml ~2.5

Suggested

  • symfony/event-dispatcher ~2.5
  • symfony/expression-language ~2.5
  • symfony/framework-bundle ~2.5

Getting Started

You need to be familiar with some of the third-party libraries that are used by Console in order to be able to make sense of anything. These libraries come from Symfony, an open source web application framework. For your convenience, the documentation for the most relevant libraries are linked below.

  • Console - Manages all aspects of the console (input and output). When you author your commands you will be targeting this library.
  • DependencyInjection - Responsible for wiring all of the dependencies together. Also makes it possible to alter the defaults provided by the library to better suit your needs.
  • EventDispatcher - A simple implementation of the mediator pattern. Enables events in the Console library and makes it possible to add a plugin system to your console application.

Creating an Application

use Box\Component\Console\Application;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

Before a new application can be created, a dependency injection container will be needed. While any instance of ContainerInterface may be used, we will be using an instance of ContainerBuilder (more detail on why later).

$container = new ContainerBuilder();

With the container, a new Application instance can now be created.

$app = new Application($container);

When an instance of ContainerBuilder is provided to Application, it will automatically set parameters and register services needed to run the console application. Application will not set parameters or register services that already exist. It is important to note that only instances of ContainerBuilder will cause Application to set the default parameters and register the default services.

Running an Application

Before we can begin process of running the console, the container must first be compiled. Compiling the container allows for some last minute processes to occur.

$container->compile();

With the compiled container, the application is ready to run.

$app->run();

When the code in the examples above are run from a script in the command line, the following output will be shown. It may be important to note that the output may vary slightly depending on the age of the documentation and what libraries were installed in addition to Console.

Console Tool

Usage:
  [options] command [arguments]

Options:
  --help           -h Display this help message
  --quiet          -q Do not output any message
  --verbose        -v|vv|vvv Increase the verbosity of messages: [...snip...]
  --version        -V Display this application version
  --ansi              Force ANSI output
  --no-ansi           Disable ANSI output
  --no-interaction -n Do not ask any interactive question

Available commands:
  help               Displays help for a command
  list               Lists commands
config
  config:current     Displays the current configuration
  config:list        Lists the registered extensions
  config:reference   Displays a configuration reference
container
  container:debug    Displays current services for an application
debug
  debug:container    Displays current services for an application

Using the Container

Application is designed around the use of the container. All functionality that is provided by Console can be found as a parameter or service within the container. As a result, all changes to the console (adding commands, adding helpers, changing defaults, etc) must also occur through the container.

Loading Resources

In order to make changes to the container, the loaders provided by the DependencyInjection library must be used. More information about how to use the DI loaders can be found on Symfony's website. While you may use any compatible loader, Console will only officially support XML and YAML for file-based loading. PHP is also supported, but not in conjunction with the bundled commands or loaders.

Loading .dist Files

In addition to the standard loaders, Console provides its own loader for special cases. Many applications make use of files that end with .dist. This file extension is used to indicate that the file is part of the distribution. A user may then make a copy of the file, drop the .dist extension, and use their version of the file with their software.

The following example will support the loading of XML and YAML files, with or without the .dist file extension.

use Box\Component\Console\Loader\Resource;
use Box\Component\Console\Loader\ResourceCollection;
use Box\Component\Console\Loader\ResourceCollectionLoader;
use Box\Component\Console\Loader\ResourceSupport;
use Symfony\Component\Config\Exception\FileLoaderLoadException;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

// load files from the current directory
$locator = new FileLocator('.');

// create a loader for xml and yaml files
$loader = new ResourceCollectionLoader(
    new LoaderResolver(
        array(
            new XmlFileLoader($container, $locator),
            new YamlFileLoader($container, $locator)
        )
    )
);

// load the first available file from a collection of possible resources
$loader->load(
    new ResourceCollection(
        array(
            new Resource('example.xml'),
            new ResourceSupport('example.xml.dist', 'example.xml'),
            new Resource('example.yml'),
            new ResourceSupport('example.yml.dist', 'example.yml')
        )
    )
);

As the name implies, the ResourceCollection class manages a collection of Resource instances. Because of how the standard file loaders behave when determining support for files, ResourceSupport is used to map an unsupported file extension (e.g. .yml.dist) a supported one (e.g. .yml). The loader, ResourceCollectionLoader, will then iterate through the collection attempting load each resource until one is successfully loaded. If the first resource in the collection fails to load due to it not existing, the next will be attempted. This iteration will continue until the list is exhausted, or an error is found while processing an available resource.

In the example above, an exception is thrown if none of the resources in the collection exist. To optionally load a resource, without having an exception thrown, the loadOptional() method should be used.

$loader->loadOptional(
    new ResourceCollection(
        array(
            new Resource('example.xml'),
            new ResourceSupport('example.xml.dist', 'example.xml'),
            new Resource('example.yml'),
            new ResourceSupport('example.yml.dist', 'example.yml')
        )
    )
);

Registering Commands

To register a command, you must tag its service with "box.console.command".

In PHP

$definition = new Definition('My\Command');
$definition->addTag('box.console.command');

$container->setDefinition('my_command', $definition);

In XML

<container>
  <services>
    <service class="My\Command" id="my_command">
      <tag name="box.console.command"/>
    </service>
  </services>
</container>

In YAML

services:

    my_command:
        class: My\Command
        tags:
            - { name: box.console.command }

Registering Helpers

To register a command, you must tag its service with "box.console.helper".

In PHP

$definition = new Definition('My\Helper');
$definition->addTag('box.console.helper');

$container->setDefinition('my_helper', $definition);

In XML

<container>
  <services>
    <service class="My\Helper" id="my_helper">
      <tag name="box.console.helper"/>
    </service>
  </services>
</container>

In YAML

services:

    my_helper:
        class: My\Helper
        tags:
            - { name: box.console.helper }

Registering Event Listeners and Subscribers

The EventDispatcher library supports the registration of listeners, or a collection of listeners through what is known as a "subscriber". A listener is a single callable, while a subscriber is a class that returns a list of which methods must be called when specific events a dispatched.

Listeners

In PHP
$definition = new Definition('My\Listener');
$definition->addTag(
    'box.console.event.listener',
    array(
        'event' => 'the.event',
        'method' => 'onEvent'
    )
);

$container->setDefinition('my_listener', $definition);
In XML
<container>
  <services>
    <service class="My\Listener" id="my_listener">
      <tag name="box.console.event.listener" event="the.event" method="onEvent"/>
    </service>
  </services>
</container>
In YAML
services:

    my_listener:
        class: My\Listener
        tags:
            - name: box.console.event.listener
              event: the.event
              method: onEvent

Subscribers

In PHP
$definition = new Definition('My\Subscriber');
$definition->addTag('box.console.event.subscriber');

$container->setDefinition('my_subscriber', $definition);
In XML
<container>
  <services>
    <service class="My\Subscriber" id="my_subscriber">
      <tag name="box.console.event.subscriber"/>
    </service>
  </services>
</container>
In YAML
services:

    my_subscriber:
        class: My\Subscriber
        tags:
            - { name: box.console.event.subscriber }

Registering Extensions

Extensions provide another way of setting parameters and setting service definitions for the container. However, in order make those extensions available to the container helper so that the bundled commands still work, registration must be done through the registerExtension() method.

$app->registerExtension(new MyExtension());

This will create a tagged service definition in the container builder, in addition to register the extension with the container. This will allow the helper to re-register the same extension when a new container builder is created for the bundled commands.

The Defaults

As mentioned early in Getting Started, when a ContainerBuilder instance is passed to Application, a set of default parameters and services are set within the container. The following is a list of those parameters and services.

Parameters

Name (Default Value) Description
box.console.auto_exit
(true)
If true, exit() is called once a command finishes.
box.console.class
(Symfony\Component\Console\Application)
The class for the console application.
box.console.command.*.class
(Instances of Symfony\Component\Console\Command\Command)
The class for each default command.
box.console.event_dispatcher.class
(Symfony\Component\EventDispatcher\ContainerAwareDispatcher)
The class for the event dispatcher.
box.console.helper.*.class
(Instances of Symfony\Component\Console\Helper\Helper)
The class for each default helper.
box.console.helper.container.class
(Box\Component\Console\Helper\ContainerHelper)
The class a helper that provides access to the container.
box.console.helper_set.class
(Symfony\Component\Console\Helper\HelperSet)
The class for the helper set.
box.console.input.class
(Symfony\Component\Console\Input\ArgvInput)
The class for the default input manager.
box.console.name
(UNKNOWN)
The name of the console application.
box.console.output.class
(Symfony\Component\Console\Output\ConsoleOutput)
The class for the default output manager.
box.console.version
(UNKNOWN)
The version of the console application.

Services

Identifier (Class) Description
box.console
(%box.console.class%)
The console application which contains all commands.
box.console.helper.container
(%box.console.helper.container.class%)
A helper that provides access to the container.
box.console.command.*
(%box.console.command.*.class%)
A command.
box.console.helper.*
(%box.console.helper.*.class%)
A helper.
box.console.event_dispatcher
(%box.console.event_dispatcher.class%)
The event dispatcher.
box.console.helper_set
(%box.console.helper_set.class%)
The helper set which contains all helpers.
box.console.input
(%box.console.input.class%)
The input manager.
box.console.output
(%box.console.output.class%)
The output manager.

Commands

Command Description
config:current Displays the current configuration for an extension registered with the container.
config:list Displays the list of extensions registered with the container.
config:reference Displays the reference configuration for an extension registered with the container.
debug:container Displays the parameters and services in the container. This command is only available if symfony/framework-bundle is installed.

Helpers

Helper Description
container Provides access to the container.

Performance

The processing of building a container can potentially be time consuming and costly in terms of performance. Console provides a way to cache the results of the container building process so that subsequent uses of the application can be faster.

use Box\Component\Console\ApplicationCache;
use Symfony\Component\DependencyInjection\ContainerBuilder;

ApplicationCache::bootstrap(
    '/path/to/cache/example.php',
    function (ContainerBuilder $container, ApplicationCache $app) {
        // first-run container building
        // register extensions using $app
    },
    'MyCachedContainer', // name of cached container class
    true                 // toggle debugging
);

The ApplicationCache::bootstrap() method manages the process of creating, loading, and saving the container. When the application is first run using this method, the following files are created. It is important to note that the name of the generated files will vary depending on what you provided as the first argument to bootstrap().

File Description
example.php The cached container.
example.php.meta The cache metadata, used to determine if the cache needs to be refreshed.
example.xml The container configuration used for debugging.

By default, the name of the cached container class is ConsoleContainer and resides in the root namespace. Also by default, "debugging" is enabled. The debugging option will cause the cache to be refreshed if a resource is updated. By disabling debugging, the cache files must be manually deleted before any of the changes to the resources take effect.

License

This software is released under the MIT license.

About

A console application assembled through dependency injection.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages