Skip to content

Type changes round4 #7926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions _build/redirection_map
Original file line number Diff line number Diff line change
Expand Up @@ -336,3 +336,5 @@
/components/dependency_injection/autowiring /service_container/autowiring
/event_dispatcher/class_extension /event_dispatcher
/security/target_path /security
/service_container/third_party /service_container
/templating/templating_service /templates
9 changes: 8 additions & 1 deletion bundles/best_practices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,14 @@ If the bundle defines services, they must be prefixed with the bundle alias.
For example, AcmeBlogBundle services must be prefixed with ``acme_blog``.

In addition, services not meant to be used by the application directly, should
be :ref:`defined as private <container-private-services>`.
be :ref:`defined as private <container-private-services>`. For public services,
:ref:`aliases should be created <service-autowiring-alias>` from the interface/class
to the service id. For example, in MonologBundle, an alias is created from
``Psr\Log\LoggerInterface`` to ``logger`` so that the ``LoggerInterface`` type-hint
can be used for autowiring.

Services should not use autowiring or autoconfiguration. Instead, all services should
be defined explicitly.

.. seealso::

Expand Down
6 changes: 6 additions & 0 deletions bundles/extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ read more about it, see the ":doc:`/bundles/configuration`" article.
Adding Classes to Compile
-------------------------

.. note::

The ``addClassesToCompile()`` method was deprecated in Symfony 3.3, and will
be removed in Symfony 4.0. If you want to use this method and be compatible
with Symfony 4.0, check to see if the method exists before calling it.

Symfony creates a big ``classes.php`` file in the cache directory to aggregate
the contents of the PHP classes that are used in every request. This reduces the
I/O operations and increases the application performance.
Expand Down
4 changes: 2 additions & 2 deletions console/command_in_controller.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ Run this command from inside your controller via::
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;

class SpoolController extends Controller
{
public function sendSpoolAction($messages = 10)
public function sendSpoolAction($messages = 10, KernelInterface $kernel)
{
$kernel = $this->get('kernel');
$application = new Application($kernel);
$application->setAutoExit(false);

Expand Down
167 changes: 29 additions & 138 deletions console/commands_as_services.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,173 +4,64 @@
How to Define Commands as Services
==================================

By default, Symfony will take a look in the ``Command`` directory of each
bundle and automatically register your commands. If a command extends the
:class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`,
Symfony will even inject the container.
While making life easier, this has some limitations:
If you're using the :ref:`default services.yml configuration <service-container-services-load-example>`,
your command classes are already registered as services. Great! This is the recommended
setup, but it's not required. Symfony also looks in the ``Command/`` directory of
each bundle and automatically registers those classes as commands.

* Your command must live in the ``Command`` directory;
* There's no way to conditionally register your command based on the environment
or availability of some dependencies;
* You can't access the container in the ``configure()`` method (because
``setContainer()`` hasn't been called yet);
* You can't use the same class to create many commands (i.e. each with
different configuration).
.. note::

To solve these problems, you can register your command as a service and tag it
with ``console.command``:
You can also manually register your command as a service by configuring the service
and :doc:`tagging it </service_container/tags>` with ``console.command``.

.. configuration-block::
In either case, if your class extends :class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand`,
you can access public services via ``$this->getContainer()->get('SERVICE_ID')``.

.. code-block:: yaml
But if your class is registered as a service, you can instead access services by
using normal :ref:`dependency injection <services-constructor-injection>`.

# app/config/config.yml
services:
AppBundle\Command\MyCommand: [console.command]
For example, suppose you want to log something from within your command::

.. code-block:: xml

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="AppBundle\Command\MyCommand">
<tag name="console.command" />
</service>
</services>

</container>

.. code-block:: php

// app/config/config.php
use AppBundle\Command\MyCommand;

$container->register(MyCommand::class)
->addTag('console.command')
;

Using Dependencies and Parameters to Set Default Values for Options
-------------------------------------------------------------------

Imagine you want to provide a default value for the ``name`` option. You could
pass one of the following as the 5th argument of ``addOption()``:

* a hardcoded string;
* a container parameter (e.g. something from ``parameters.yml``);
* a value computed by a service (e.g. a repository).

By extending ``ContainerAwareCommand``, only the first is possible, because you
can't access the container inside the ``configure()`` method. Instead, inject
any parameter or service you need into the constructor. For example, suppose you
store the default value in some ``%command.default_name%`` parameter::

// src/AppBundle/Command/GreetCommand.php
namespace AppBundle\Command;

use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class GreetCommand extends Command
class SunshineCommand extends Command
{
protected $defaultName;
private $logger;

public function __construct($defaultName)
public function __construct(LoggerInterface $logger)
{
$this->defaultName = $defaultName;
$this->logger = $logger;

// you *must* call the parent constructor
parent::__construct();
}

protected function configure()
{
// try to avoid work here (e.g. database query)
// this method is *always* called - see warning below
$defaultName = $this->defaultName;

$this
->setName('demo:greet')
->setDescription('Greet someone')
->addOption(
'name',
'-n',
InputOption::VALUE_REQUIRED,
'Who do you want to greet?',
$defaultName
)
;
->setName('app:sunshine')
->setDescription('Good morning!');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$name = $input->getOption('name');

$output->writeln($name);
$this->logger->info('Waking up the sun');
// ...
}
}

Now, just update the arguments of your service configuration like normal to
inject the ``command.default_name`` parameter:

.. configuration-block::

.. code-block:: yaml

# app/config/config.yml
parameters:
command.default_name: Javier

services:
AppBundle\Command\MyCommand:
arguments: ["%command.default_name%"]
tags: [console.command]

.. code-block:: xml

<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="command.default_name">Javier</parameter>
</parameters>

<services>
<service class="AppBundle\Command\MyCommand">
<argument>%command.default_name%</argument>
<tag name="console.command" />
</service>
</services>

</container>

.. code-block:: php

// app/config/config.php
use AppBundle\Command\MyCommand;

$container->setParameter('command.default_name', 'Javier');

$container
->register(MyCommand::class)
->setArguments(array('%command.default_name%'))
->addTag('console.command')
;

Great, you now have a dynamic default value!
If you're using the :ref:`default services.yml configuration <service-container-services-load-example>`,
the command class will automatically be registered as a service and passed the ``$logger``
argument (thanks to autowiring). In other words, *just* by creating this class, everything
works! You can call the ``app:sunshine`` command and start logging.

.. caution::

Be careful not to actually do any work in ``configure`` (e.g. make database
queries), as your code will be run, even if you're using the console to
execute a different command.
You *do* have access to services in ``configure()``. However, try to avoid doing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to upgrade this paragraph when lazy console commands are merged in Symfony 4. There's a PR for that: symfony/symfony#22734

any work (e.g. making database queries), as that code will be run, even if you're
using the console to execute a different command.
3 changes: 3 additions & 0 deletions console/logging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ listener for the console.

Starting from Symfony 3.3, the Console component provides automatic error and
exception logging.

You can of course also access and use the :doc:`logger </logging>` service to
log messages.
Loading