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();
kherge/file
~1.3herrera-io/object-storage
~1.0symfony/config
~2.5symfony/console
~2.5symfony/dependency-injection
~2.5symfony/yaml
~2.5
symfony/event-dispatcher
~2.5symfony/expression-language
~2.5symfony/framework-bundle
~2.5
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.
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.
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
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.
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.
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')
)
)
);
To register a command, you must tag its service with "box.console.command".
$definition = new Definition('My\Command');
$definition->addTag('box.console.command');
$container->setDefinition('my_command', $definition);
<container>
<services>
<service class="My\Command" id="my_command">
<tag name="box.console.command"/>
</service>
</services>
</container>
services:
my_command:
class: My\Command
tags:
- { name: box.console.command }
To register a command, you must tag its service with "box.console.helper".
$definition = new Definition('My\Helper');
$definition->addTag('box.console.helper');
$container->setDefinition('my_helper', $definition);
<container>
<services>
<service class="My\Helper" id="my_helper">
<tag name="box.console.helper"/>
</service>
</services>
</container>
services:
my_helper:
class: My\Helper
tags:
- { name: box.console.helper }
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.
$definition = new Definition('My\Listener');
$definition->addTag(
'box.console.event.listener',
array(
'event' => 'the.event',
'method' => 'onEvent'
)
);
$container->setDefinition('my_listener', $definition);
<container>
<services>
<service class="My\Listener" id="my_listener">
<tag name="box.console.event.listener" event="the.event" method="onEvent"/>
</service>
</services>
</container>
services:
my_listener:
class: My\Listener
tags:
- name: box.console.event.listener
event: the.event
method: onEvent
$definition = new Definition('My\Subscriber');
$definition->addTag('box.console.event.subscriber');
$container->setDefinition('my_subscriber', $definition);
<container>
<services>
<service class="My\Subscriber" id="my_subscriber">
<tag name="box.console.event.subscriber"/>
</service>
</services>
</container>
services:
my_subscriber:
class: My\Subscriber
tags:
- { name: box.console.event.subscriber }
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.
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.
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. |
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. |
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. |
Helper | Description |
---|---|
container |
Provides access to the container. |
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.
This software is released under the MIT license.