diff --git a/.travis.yml b/.travis.yml
index c20155875c2..9afcdfaaff4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,6 @@ python:
- "2.7"
install:
- - "git submodule update --init"
- "bash install.sh"
- "pip install -q -r requirements.txt --use-mirrors"
diff --git a/book/installation.rst b/book/installation.rst
index 7c5c80560e9..d6908290464 100644
--- a/book/installation.rst
+++ b/book/installation.rst
@@ -233,7 +233,7 @@ If there are any issues, correct them now before moving on.
$ rm -rf app/cache/*
$ rm -rf app/logs/*
- $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data' | grep -v root | head -1 | cut -d\ -f1`
+ $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1`
$ sudo chmod +a "$APACHEUSER allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs
$ sudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs
@@ -248,7 +248,7 @@ If there are any issues, correct them now before moving on.
.. code-block:: bash
- $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data' | grep -v root | head -1 | cut -d\ -f1`
+ $ APACHEUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1`
$ sudo setfacl -Rn -m u:"$APACHEUSER":rwX -m u:`whoami`:rwX app/cache app/logs
$ sudo setfacl -dRn -m u:"$APACHEUSER":rwX -m u:`whoami`:rwX app/cache app/logs
diff --git a/cookbook/console/commands_as_services.rst b/cookbook/console/commands_as_services.rst
new file mode 100644
index 00000000000..c6001e246bf
--- /dev/null
+++ b/cookbook/console/commands_as_services.rst
@@ -0,0 +1,122 @@
+.. index::
+ single: Console; Commands as Services
+
+How to Define Commands as Services
+==================================
+
+.. versionadded:: 2.4
+ Support for registering commands in the service container was introduced in
+ version 2.4.
+
+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:
+
+* Your command must live in the ``Command`` directory;
+* There's no way to conditionally register your service 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).
+
+To solve these problems, you can register your command as a service and tag it
+with ``console.command``:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # app/config/config.yml
+ services:
+ acme_hello.command.my_command:
+ class: Acme\HelloBundle\Command\MyCommand
+ tags:
+ - { name: console.command }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // app/config/config.php
+ $container
+ ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand')
+ ->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
+have some ``NameRepository`` service that you'll use to get your default value::
+
+ // src/Acme/DemoBundle/Command/GreetCommand.php
+ namespace Acme\DemoBundle\Command;
+
+ use Acme\DemoBundle\Entity\NameRepository;
+ 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
+ {
+ protected $nameRepository;
+
+ public function __construct(NameRepository $nameRepository)
+ {
+ $this->nameRepository = $nameRepository;
+ }
+
+ protected function configure()
+ {
+ $defaultName = $this->nameRepository->findLastOne();
+
+ $this
+ ->setName('demo:greet')
+ ->setDescription('Greet someone')
+ ->addOption('name', '-n', InputOption::VALUE_REQUIRED, 'Who do you want to greet?', $defaultName)
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $name = $input->getOption('name');
+
+ $output->writeln($name);
+ }
+ }
+
+Now, just update the arguments of your service configuration like normal to
+inject the ``NameRepository``. Great, you now have a dynamic default value!
+
+.. 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.
diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst
index fc903b12650..30ea6f0d9cf 100644
--- a/cookbook/console/console_command.rst
+++ b/cookbook/console/console_command.rst
@@ -65,55 +65,11 @@ This command will now automatically be available to run:
.. _cookbook-console-dic:
Register Commands in the Service Container
-------------------------------------------
-
-.. versionadded:: 2.4
- Support for registering commands in the service container was added in
- version 2.4.
-
-Instead of putting your command in the ``Command`` directory and having Symfony
-auto-discover it for you, you can register commands in the service container
-using the ``console.command`` tag:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- services:
- acme_hello.command.my_command:
- class: Acme\HelloBundle\Command\MyCommand
- tags:
- - { name: console.command }
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
-
- $container
- ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand')
- ->addTag('console.command')
- ;
-
-.. tip::
+-------------------------------------------
- Registering your command as a service gives you more control over its
- location and the services that are injected into it. But, there are no
- functional advantages, so you don't need to register your command as a service.
+Just like controllers, commands can be declared as services. See the
+:doc:`dedicated cookbook entry `
+for details.
Getting Services from the Service Container
-------------------------------------------
diff --git a/cookbook/console/index.rst b/cookbook/console/index.rst
index 878d1fc862a..6f1f939d772 100644
--- a/cookbook/console/index.rst
+++ b/cookbook/console/index.rst
@@ -8,3 +8,4 @@ Console
usage
sending_emails
logging
+ commands_as_services
diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst
index 5c966d5b2eb..c100fb52210 100644
--- a/cookbook/form/dynamic_form_modification.rst
+++ b/cookbook/form/dynamic_form_modification.rst
@@ -99,7 +99,7 @@ creating that particular field is delegated to an event listener::
{
$builder->add('price');
- $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
+ $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
// ... adding the name field if needed
});
}
@@ -116,7 +116,7 @@ the event listener might look like the following::
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
- $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
+ $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
@@ -249,7 +249,7 @@ Using an event listener, your form might look like this::
->add('subject', 'text')
->add('body', 'textarea')
;
- $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
+ $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
// ... add a choice list of friends of the current application user
});
}
@@ -325,13 +325,13 @@ and fill in the listener logic::
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
- function(FormEvent $event) use ($user) {
+ function (FormEvent $event) use ($user) {
$form = $event->getForm();
$formOptions = array(
'class' => 'Acme\DemoBundle\Entity\User',
'property' => 'fullName',
- 'query_builder' => function(EntityRepository $er) use ($user) {
+ 'query_builder' => function (EntityRepository $er) use ($user) {
// build a custom query
// return $er->createQueryBuilder('u')->addOrderBy('fullName', 'DESC');
@@ -491,7 +491,7 @@ sport like this::
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
- function(FormEvent $event) {
+ function (FormEvent $event) {
$form = $event->getForm();
// this would be your entity, i.e. SportMeetup
@@ -556,7 +556,7 @@ The type would now look like::
));
;
- $formModifier = function(FormInterface $form, Sport $sport = null) {
+ $formModifier = function (FormInterface $form, Sport $sport = null) {
$positions = null === $sport ? array() : $sport->getAvailablePositions();
$form->add('position', 'entity', array(
@@ -568,7 +568,7 @@ The type would now look like::
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
- function(FormEvent $event) use ($formModifier) {
+ function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
@@ -578,7 +578,7 @@ The type would now look like::
$builder->get('sport')->addEventListener(
FormEvents::POST_SUBMIT,
- function(FormEvent $event) use ($formModifier) {
+ function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$sport = $event->getForm()->getData();
@@ -730,7 +730,7 @@ all of this, use a listener::
public function buildForm(FormBuilderInterface $builder, array $options)
{
- $builder->addEventListener(FormEvents::POST_SUBMIT, function($event) {
+ $builder->addEventListener(FormEvents::POST_SUBMIT, function ($event) {
$event->stopPropagation();
}, 900); // Always set a higher priority than ValidationListener
diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst
index e27a86669be..b9e3afdb025 100644
--- a/cookbook/security/voters_data_permission.rst
+++ b/cookbook/security/voters_data_permission.rst
@@ -66,7 +66,6 @@ edit a particular object. Here's an example implementation:
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
- use Acme\DemoBundle\Entity\Post;
class PostVoter implements VoterInterface
{
@@ -81,9 +80,11 @@ edit a particular object. Here's an example implementation:
));
}
- public function supportsClass($obj)
+ public function supportsClass($class)
{
- return $obj instanceof Post;
+ $supportedClass = 'Acme\DemoBundle\Entity\Post';
+
+ return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
/**
@@ -92,7 +93,7 @@ edit a particular object. Here's an example implementation:
public function vote(TokenInterface $token, $post, array $attributes)
{
// check if class of this object is supported by this voter
- if (!$this->supportsClass($post)) {
+ if (!$this->supportsClass(get_class($post))) {
return VoterInterface::ACCESS_ABSTAIN;
}