diff --git a/_build/redirection_map b/_build/redirection_map index d1143a223ec..66d84966a79 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -77,6 +77,7 @@ /book/configuration /configuration /book/propel /propel/propel /book/performance /performance +/bundles/installation /bundles /cookbook/assetic/apply_to_option /frontend/assetic/apply_to_option /cookbook/assetic/asset_management /frontend/assetic/asset_management /cookbook/assetic/index /frontend/assetic @@ -96,10 +97,11 @@ /cookbook/bundles/extension /bundles/extension /cookbook/bundles/index /bundles /cookbook/bundles/inheritance /bundles/inheritance -/cookbook/bundles/installation /bundles/installation +/cookbook/bundles/installation /bundles /cookbook/bundles/override /bundles/override /cookbook/bundles/prepend_extension /bundles/prepend_extension -/cookbook/bundles/remove /bundles/remove +/cookbook/bundles/remove /bundles +/bundles/remove /bundles /cookbook/cache/form_csrf_caching /http_cache/form_csrf_caching /cookbook/cache/varnish /http_cache/varnish /cookbook/composer /setup/composer @@ -120,14 +122,16 @@ /cookbook/console/logging /console/logging /cookbook/console/request_context /console/request_context /cookbook/console/style /console/style -/cookbook/console/usage /console/usage -/cookbook/controller/csrf_token_validation /controller/csrf_token_validation +/cookbook/console/usage /console +/console/usage /console +/cookbook/controller/csrf_token_validation /security/csrf /cookbook/controller/error_pages /controller/error_pages /cookbook/controller/forwarding /controller/forwarding /cookbook/controller/index /controller /cookbook/controller/service /controller/service /cookbook/controller/upload_file /controller/upload_file -/cookbook/debugging /debug/debugging +/cookbook/debugging / +/debug/debugging / /cookbook/deployment/azure-website /cookbook/azure-website /cookbook/deployment/fortrabbit /deployment/fortrabbit /cookbook/deployment/heroku /deployment/heroku @@ -135,22 +139,25 @@ /cookbook/deployment/platformsh /deployment/platformsh /cookbook/deployment/tools /deployment/tools /cookbook/doctrine/common_extensions /doctrine/common_extensions -/cookbook/doctrine/console /doctrine/console +/cookbook/doctrine/console /doctrine /cookbook/doctrine/custom_dql_functions /doctrine/custom_dql_functions /cookbook/doctrine/dbal /doctrine/dbal /cookbook/doctrine/event_listeners_subscribers /doctrine/event_listeners_subscribers /cookbook/doctrine/index /doctrine -/cookbook/doctrine/mapping_model_classes /doctrine/mapping_model_classes +/cookbook/doctrine/mapping_model_classes /doctrine +/doctrine/mapping_model_classes /doctrine /cookbook/doctrine/mongodb_session_storage /doctrine/mongodb_session_storage /cookbook/doctrine/multiple_entity_managers /doctrine/multiple_entity_managers /cookbook/doctrine/pdo_session_storage /doctrine/pdo_session_storage /cookbook/doctrine/registration_form /doctrine/registration_form /cookbook/doctrine/resolve_target_entity /doctrine/resolve_target_entity /cookbook/doctrine/reverse_engineering /doctrine/reverse_engineering -/cookbook/email/cloud /email/cloud +/doctrine/repository /doctrine +/doctrine/console /doctrine +/cookbook/email/cloud /email /cookbook/email/dev_environment /email/dev_environment /cookbook/email/email /email -/cookbook/email/gmail /email/gmail +/cookbook/email/gmail /email /cookbook/email/index /email /cookbook/email/spool /email/spool /cookbook/email/testing /email/testing @@ -214,7 +221,7 @@ /cookbook/security/acl /security/acl /cookbook/security/acl_advanced /security/acl_advanced /cookbook/security/api_key_authentication /security/api_key_authentication -/cookbook/security/csrf_in_login_form /security/csrf_in_login_form +/cookbook/security/csrf_in_login_form /security/csrf /cookbook/security/custom_authentication_provider /security/custom_authentication_provider /cookbook/security/custom_password_authenticator /security/custom_password_authenticator /cookbook/security/custom_provider /security/custom_provider @@ -280,18 +287,28 @@ /cookbook/web_services/php_soap_extension /controller/soap_web_service /cookbook/workflow/homestead /setup/homestead /cookbook/workflow/index /setup -/cookbook/workflow/new_project_git /setup/new_project_git -/cookbook/workflow/new_project_svn /setup/new_project_svn +/cookbook/workflow/new_project_git /setup +/cookbook/workflow/new_project_svn /setup +/setup/new_project_git /setup +/setup/new_project_svn /setup /components/asset/index /components/asset /components/asset/introduction /components/asset /components/browser_kit/index /components/browser_kit /components/browser_kit/introduction /components/browser_kit /components/class_loader/introduction /components/class_loader /components/class_loader/index /components/class_loader +/components/class_loader/cache_class_loader /components/class_loader +/components/class_loader/class_loader /components/class_loader +/components/class_loader/class_map_generator /components/class_loader +/components/class_loader/debug_class_loader /components/class_loader +/components/class_loader/map_class_loader /components/class_loader +/components/class_loader/map_class_loader /components/class_loader +/components/class_loader/psr4_class_loader /components/class_loader /components/config/introduction /components/config /components/config/index /components/config /components/console/helpers/tablehelper /components/console/helpers/table /components/console/helpers/progresshelper /components/console/helpers/progressbar +/components/console/helpers/dialoghelper /components/console/helpers/questionhelper /components/console/introduction /components/console /components/console/index /components/console /components/debug/class_loader /components/debug @@ -343,16 +360,31 @@ /components/var_dumper/index /components/var_dumper /components/yaml/introduction /components/yaml /components/yaml/index /components/yaml +/console/logging /console +/controller/csrf_token_validation /security/csrf /deployment/tools /deployment +/form/csrf_protection /security/csrf /install/bundles /setup/bundles +/email/gmail /email +/email/cloud /email /event_dispatcher/class_extension /event_dispatcher /form /forms /form/use_virtual_forms /form/inherit_data_option +/frontend/assetic/apply_to_option /frontend/assetic +/frontend/assetic/asset_management /frontend/assetic +/frontend/assetic/jpeg_optimize /frontend/assetic +/frontend/assetic/php /frontend/assetic +/frontend/assetic/uglifyjs /frontend/assetic +/frontend/assetic/yuicompressor /frontend/assetic +/reference/configuration/assetic /frontend/assetic /security/target_path /security +/security/csrf_in_login_form /security/csrf /service_container/service_locators /service_container/service_subscribers_locators /service_container/third_party /service_container /templating/templating_service /templates /testing/simulating_authentication /testing/http_authentication /validation/group_service_resolver /form/validation_group_service_resolver /request/load_balancer_reverse_proxy /deployment/proxies +/quick_tour/the_controller /quick_tour/the_big_picture +/quick_tour/the_view /quick_tour/flex_recipes /service_container/service_locators /service_container/service_subscribers_locators diff --git a/_images/components/messenger/overview.png b/_images/components/messenger/overview.png new file mode 100644 index 00000000000..074255b4667 Binary files /dev/null and b/_images/components/messenger/overview.png differ diff --git a/_images/components/workflow/blogpost_puml.png b/_images/components/workflow/blogpost_puml.png new file mode 100644 index 00000000000..14d45c8b40f Binary files /dev/null and b/_images/components/workflow/blogpost_puml.png differ diff --git a/_images/quick_tour/no_routes_page.png b/_images/quick_tour/no_routes_page.png new file mode 100644 index 00000000000..8c8c4d508d1 Binary files /dev/null and b/_images/quick_tour/no_routes_page.png differ diff --git a/_images/quick_tour/profiler.png b/_images/quick_tour/profiler.png deleted file mode 100644 index 3b55b75f3af..00000000000 Binary files a/_images/quick_tour/profiler.png and /dev/null differ diff --git a/_images/quick_tour/web_debug_toolbar.png b/_images/quick_tour/web_debug_toolbar.png index 72cd7483f2f..465020380cb 100644 Binary files a/_images/quick_tour/web_debug_toolbar.png and b/_images/quick_tour/web_debug_toolbar.png differ diff --git a/_images/quick_tour/welcome.png b/_images/quick_tour/welcome.png deleted file mode 100644 index 738105f715d..00000000000 Binary files a/_images/quick_tour/welcome.png and /dev/null differ diff --git a/_images/translation/debug_1.png b/_images/translation/debug_1.png deleted file mode 100644 index 8f175f4d7ff..00000000000 Binary files a/_images/translation/debug_1.png and /dev/null differ diff --git a/_images/translation/debug_2.png b/_images/translation/debug_2.png deleted file mode 100644 index 04a57fa41d4..00000000000 Binary files a/_images/translation/debug_2.png and /dev/null differ diff --git a/_images/translation/debug_3.png b/_images/translation/debug_3.png deleted file mode 100644 index 6ed595e097b..00000000000 Binary files a/_images/translation/debug_3.png and /dev/null differ diff --git a/_images/translation/debug_4.png b/_images/translation/debug_4.png deleted file mode 100644 index db642b1773f..00000000000 Binary files a/_images/translation/debug_4.png and /dev/null differ diff --git a/_includes/service_container/_my_mailer.rst.inc b/_includes/service_container/_my_mailer.rst.inc index 37a20edf4a9..decb1c1a23b 100644 --- a/_includes/service_container/_my_mailer.rst.inc +++ b/_includes/service_container/_my_mailer.rst.inc @@ -2,15 +2,15 @@ .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: app.mailer: - class: AppBundle\Mailer + class: App\Mailer arguments: [sendmail] .. code-block:: xml - + - + sendmail @@ -26,8 +26,8 @@ .. code-block:: php - // app/config/services.php - use AppBundle\Mailer; + // config/services.php + use App\Mailer; $container->register('app.mailer', Mailer::class) ->addArgument('sendmail'); diff --git a/assetic/_standard_edition_warning.rst.inc b/assetic/_standard_edition_warning.rst.inc deleted file mode 100644 index 2a111cd2291..00000000000 --- a/assetic/_standard_edition_warning.rst.inc +++ /dev/null @@ -1,5 +0,0 @@ -.. caution:: - - Starting from Symfony 2.8, Assetic is no longer included by default in the - Symfony Standard Edition. Refer to :doc:`this article ` - to learn how to install and enable Assetic in your Symfony application. diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index 17cf817de7d..8eb08af3314 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -10,92 +10,55 @@ your app that's not specific to the framework (e.g. routing and controllers). Domain classes, Doctrine entities and regular PHP classes that are used as services are good examples of business logic. -For most projects, you should store everything inside the AppBundle. +For most projects, you should store all your code inside the ``src/`` directory. Inside here, you can create whatever directories you want to organize things: .. code-block:: text symfony-project/ - ├─ app/ + ├─ config/ + ├─ public/ ├─ src/ - │ └─ AppBundle/ - │ └─ Utils/ - │ └─ MyClass.php + │ └─ Utils/ + │ └─ MyClass.php ├─ tests/ ├─ var/ - ├─ vendor/ - └─ web/ + └─ vendor/ -Storing Classes Outside of the Bundle? --------------------------------------- +.. _services-naming-and-format: -But there's no technical reason for putting business logic inside of a bundle. -If you like, you can create your own namespace inside the ``src/`` directory -and put things there: +Services: Naming and Configuration +---------------------------------- -.. code-block:: text - - symfony-project/ - ├─ app/ - ├─ src/ - │ ├─ Acme/ - │ │ └─ Utils/ - │ │ └─ MyClass.php - │ └─ AppBundle/ - ├─ tests/ - ├─ var/ - ├─ vendor/ - └─ web/ - -.. tip:: +.. best-practice:: - The recommended approach of using the ``AppBundle/`` directory is for - simplicity. If you're advanced enough to know what needs to live in - a bundle and what can live outside of one, then feel free to do that. + Use autowiring to automate the configuration of application services. -Services: Naming and Format ---------------------------- +:doc:`Service autowiring ` is a feature provided +by Symfony's Service Container to manage services with minimal configuration. It +reads the type-hints on your constructor (or other methods) and automatically +passes the correct services to each method. It can also add +:doc:`service tags ` to the services needing them, such +as Twig extensions, event subscribers, etc. The blog application needs a utility that can transform a post title (e.g. -"Hello World") into a slug (e.g. "hello-world"). The slug will be used as -part of the post URL. +"Hello World") into a slug (e.g. "hello-world") to include it as part of the +post URL. Let's create a new ``Slugger`` class inside ``src/Utils/``:: -Let's create a new ``Slugger`` class inside ``src/AppBundle/Utils/`` and -add the following ``slugify()`` method:: - - // src/AppBundle/Utils/Slugger.php - namespace AppBundle\Utils; + // src/Utils/Slugger.php + namespace App\Utils; class Slugger { - public function slugify($string) + public function slugify(string $value): string { - return preg_replace( - '/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string))) - ); + // ... } } -Next, define a new service for that class. - -.. code-block:: yaml - - # app/config/services.yml - services: - # ... - - # use the fully-qualified class name as the service id - AppBundle\Utils\Slugger: - public: false - -.. note:: - - If you're using the :ref:`default services.yml configuration `, - the class is auto-registered as a service. - -Traditionally, the naming convention for a service was a short, but unique -snake case key - e.g. ``app.utils.slugger``. But for most services, you should now -use the class name. +If you're using the :ref:`default services.yaml configuration `, +this class is auto-registered as a service whose ID is ``App\Utils\Slugger`` (or +simply ``Slugger::class`` if the class is already imported in your code). .. best-practice:: @@ -103,19 +66,15 @@ use the class name. except when you have multiple services configured for the same class (in that case, use a snake case id). -Now you can use the custom slugger in any controller class, such as the -``AdminController``:: +Now you can use the custom slugger in any other service or controller class, +such as the ``AdminController``:: - use AppBundle\Utils\Slugger; + use App\Utils\Slugger; - public function createAction(Request $request, Slugger $slugger) + public function create(Request $request, Slugger $slugger) { // ... - // you can also fetch a public service like this - // but fetching services in this way is not considered a best practice - // $slugger = $this->get('app.slugger'); - if ($form->isSubmitted() && $form->isValid()) { $slug = $slugger->slugify($post->getTitle()); $post->setSlug($slug); @@ -125,7 +84,7 @@ Now you can use the custom slugger in any controller class, such as the } Services can also be :ref:`public or private `. If you use the -:ref:`default services.yml configuration `, +:ref:`default services.yaml configuration `, all services are private by default. .. best-practice:: @@ -151,36 +110,6 @@ personal taste. We recommend YAML because it's friendly to newcomers and concise. You can of course use whatever format you like. -Service: No Class Parameter ---------------------------- - -You may have noticed that the previous service definition doesn't configure -the class namespace as a parameter: - -.. code-block:: yaml - - # app/config/services.yml - - # service definition with class namespace as parameter - parameters: - slugger.class: AppBundle\Utils\Slugger - - services: - app.slugger: - class: '%slugger.class%' - -This practice is cumbersome and completely unnecessary for your own services. - -.. best-practice:: - - Don't define parameters for the classes of your services. - -This practice was wrongly adopted from third-party bundles. When Symfony -introduced its service container, some developers used this technique to easily -allow overriding services. However, overriding a service by just changing its -class name is a very rare use case because, frequently, the new service has -different constructor arguments. - Using a Persistence Layer ------------------------- @@ -192,7 +121,7 @@ library or strategy you want for this. In practice, many Symfony applications rely on the independent `Doctrine project`_ to define their model using entities and repositories. Just like with business logic, we recommend storing Doctrine entities in the -AppBundle. +``src/Entity/`` directory. The three entities defined by our sample blog application are a good example: @@ -201,16 +130,10 @@ The three entities defined by our sample blog application are a good example: symfony-project/ ├─ ... └─ src/ - └─ AppBundle/ - └─ Entity/ - ├─ Comment.php - ├─ Post.php - └─ User.php - -.. tip:: - - If you're more advanced, you can of course store them under your own - namespace in ``src/``. + └─ Entity/ + ├─ Comment.php + ├─ Post.php + └─ User.php Doctrine Mapping Information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -227,7 +150,7 @@ PHP and annotations. Annotations are by far the most convenient and agile way of setting up and looking for mapping information:: - namespace AppBundle\Entity; + namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; @@ -303,29 +226,15 @@ the following command to install the Doctrine fixtures bundle: $ composer require "doctrine/doctrine-fixtures-bundle" -Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and +Then, this bundle is enabled automatically, but only for the ``dev`` and ``test`` environments:: - use Symfony\Component\HttpKernel\Kernel; - - class AppKernel extends Kernel - { - public function registerBundles() - { - $bundles = array( - // ... - ); - - if (in_array($this->getEnvironment(), array('dev', 'test'))) { - // ... - $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(); - } - - return $bundles; - } + // config/bundles.php + return [ // ... - } + Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], + ]; We recommend creating just *one* `fixture class`_ for simplicity, though you're welcome to have more if that class gets quite large. @@ -340,7 +249,7 @@ command: Careful, database will be purged. Do you want to continue Y/N ? Y > purging database - > loading AppBundle\DataFixtures\ORM\LoadFixtures + > loading App\DataFixtures\ORM\LoadFixtures Coding Standards ---------------- diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst index 15d8ebbe312..bfa767f0d04 100644 --- a/best_practices/configuration.rst +++ b/best_practices/configuration.rst @@ -11,37 +11,44 @@ three parts. Infrastructure-Related Configuration ------------------------------------ +These are the options that change from one machine to another (e.g. from your +development machine to the production server) but which don't change the +application behavior. + .. best-practice:: - Define the infrastructure-related configuration options in the - ``app/config/parameters.yml`` file. + Define the infrastructure-related configuration options as environment + variables. During development, use the ``.env`` file at the root of your + project to set these. -The default ``parameters.yml`` file follows this recommendation and defines the -options related to the database and mail server infrastructure: +By default, Symfony adds these types of options to the ``.env`` file when +installing new dependencies in the app: -.. code-block:: yaml +.. code-block:: bash - # app/config/parameters.yml - parameters: - database_driver: pdo_mysql - database_host: 127.0.0.1 - database_port: ~ - database_name: symfony - database_user: root - database_password: ~ + # .env + ###> doctrine/doctrine-bundle ### + DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/blog.sqlite + ###< doctrine/doctrine-bundle ### - mailer_transport: smtp - mailer_host: 127.0.0.1 - mailer_user: ~ - mailer_password: ~ + ###> symfony/swiftmailer-bundle ### + MAILER_URL=smtp://localhost?encryption=ssl&auth_mode=login&username=&password= + ###< symfony/swiftmailer-bundle ### - # ... + # ... -These options aren't defined inside the ``app/config/config.yml`` file because +These options aren't defined inside the ``config/services.yaml`` file because they have nothing to do with the application's behavior. In other words, your application doesn't care about the location of your database or the credentials to access to it, as long as the database is correctly configured. +.. caution:: + + Beware that dumping the contents of the ``$_SERVER`` and ``$_ENV`` variables + or outputting the ``phpinfo()`` contents will display the values of the + environment variables, exposing sensitive information such as the database + credentials. + .. _best-practices-canonical-parameters: Canonical Parameters @@ -49,19 +56,14 @@ Canonical Parameters .. best-practice:: - Define all your application's parameters in the - ``app/config/parameters.yml.dist`` file. + Define all your application's env vars in the ``.env.dist`` file. -Symfony includes a configuration file called ``parameters.yml.dist``, which -stores the canonical list of configuration parameters for the application. +Symfony includes a configuration file called ``.env.dist`` at the project root, +which stores the canonical list of environment variables for the application. -Whenever a new configuration parameter is defined for the application, you -should also add it to this file and submit the changes to your version control -system. Then, whenever a developer updates the project or deploys it to a server, -Symfony will check if there is any difference between the canonical -``parameters.yml.dist`` file and your local ``parameters.yml`` file. If there -is a difference, Symfony will ask you to provide a value for the new parameter -and it will add it to your local ``parameters.yml`` file. +Whenever a new env var is defined for the application, you should also add it to +this file and submit the changes to your version control system so your +workmates can update their ``.env`` files. Application-Related Configuration --------------------------------- @@ -69,17 +71,17 @@ Application-Related Configuration .. best-practice:: Define the application behavior related configuration options in the - ``app/config/config.yml`` file. + ``config/services.yaml`` file. -The ``config.yml`` file contains the options used by the application to modify -its behavior, such as the sender of email notifications, or the enabled -`feature toggles`_. Defining these values in ``parameters.yml`` file would -add an extra layer of configuration that's not needed because you don't need -or want these configuration values to change on each server. +The ``services.yaml`` file contains the options used by the application to +modify its behavior, such as the sender of email notifications, or the enabled +`feature toggles`_. Defining these values in ``.env`` file would add an extra +layer of configuration that's not needed because you don't need or want these +configuration values to change on each server. -The configuration options defined in the ``config.yml`` file usually vary from -one :doc:`environment ` to another. That's -why Symfony already includes ``app/config/config_dev.yml`` and ``app/config/config_prod.yml`` +The configuration options defined in the ``services.yaml`` may vary from one +:doc:`environment ` to another. That's why Symfony +supports defining ``config/services_dev.yaml`` and ``config/services_prod.yaml`` files so that you can override specific values for each environment. Constants vs Configuration Options @@ -99,7 +101,7 @@ to control the number of posts to display on the blog homepage: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml parameters: homepage.number_of_items: 10 @@ -109,8 +111,8 @@ option for a value that you are never going to configure just isn't necessary. Our recommendation is to define these values as constants in your application. You could, for example, define a ``NUMBER_OF_ITEMS`` constant in the ``Post`` entity:: - // src/AppBundle/Entity/Post.php - namespace AppBundle\Entity; + // src/Entity/Post.php + namespace App\Entity; class Post { @@ -135,10 +137,10 @@ Constants can be used for example in your Twig templates thanks to the And Doctrine entities and repositories can now easily access these values, whereas they cannot access the container parameters:: - namespace AppBundle\Repository; + namespace App\Repository; + use App\Entity\Post; use Doctrine\ORM\EntityRepository; - use AppBundle\Entity\Post; class PostRepository extends EntityRepository { @@ -165,7 +167,7 @@ just one or two words to describe the purpose of the parameter: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml parameters: # don't do this: 'dir' is too generic and it doesn't convey any meaning app.dir: '...' @@ -176,43 +178,6 @@ just one or two words to describe the purpose of the parameter: app.dir.contents: '...' app.contents-dir: '...' -Semantic Configuration: Don't Do It ------------------------------------ - -.. best-practice:: - - Don't define a semantic dependency injection configuration for your bundles. - -As explained in :doc:`/bundles/extension` article, Symfony bundles -have two choices on how to handle configuration: normal service configuration -through the ``services.yml`` file and semantic configuration through a special -``*Extension`` class. - -Although semantic configuration is much more powerful and provides nice features -such as configuration validation, the amount of work needed to define that -configuration isn't worth it for bundles that aren't meant to be shared as -third-party bundles. - -Moving Sensitive Options Outside of Symfony Entirely ----------------------------------------------------- - -When dealing with sensitive options, like database credentials, we also recommend -that you store them outside the Symfony project and make them available -through environment variables: - -.. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - # ... - password: "%env(DB_PASSWORD)%" - -.. versionadded:: 3.2 - Support for runtime environment variables via the ``%env(...)%`` syntax - was added in Symfony 3.2. Prior to version 3.2, you needed to use the - :doc:`special SYMFONY__ variables `. - ---- Next: :doc:`/best_practices/business-logic` diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst index 13beba910bd..cc45b565b72 100644 --- a/best_practices/controllers.rst +++ b/best_practices/controllers.rst @@ -5,16 +5,15 @@ Symfony follows the philosophy of *"thin controllers and fat models"*. This means that controllers should hold just the thin layer of *glue-code* needed to coordinate the different parts of the application. -As a rule of thumb, you should follow the 5-10-20 rule, where controllers should -only define 5 variables or less, contain 10 actions or less and include 20 lines -of code or less in each action. This isn't an exact science, but it should -help you realize when code should be refactored out of the controller and -into a service. +Your controller methods should just call to other services, trigger some events +if needed and then return a response, but they should not contain any actual +business logic. If they do, refactor it out of the controller and into a service. .. best-practice:: - Make your controller extend the FrameworkBundle base controller and use - annotations to configure routing, caching and security whenever possible. + Make your controller extend the ``AbstractController`` base controller + provided by Symfony and use annotations to configure routing, caching and + security whenever possible. Coupling the controllers to the underlying framework allows you to leverage all of its features and increases your productivity. @@ -33,6 +32,18 @@ Overall, this means you should aggressively decouple your business logic from the framework while, at the same time, aggressively coupling your controllers and routing *to* the framework in order to get the most out of it. +Controller Action Naming +------------------------ + +.. best-practice:: + + Don't add the ``Action`` suffix to the methods of the controller actions. + +The first Symfony versions required that controller method names ended in +``Action`` (e.g. ``newAction()``, ``showAction()``). This suffix became optional +when annotations were introduced for controllers. In modern Symfony applications +this suffix is neither required nor recommended, so you can safely remove it. + Routing Configuration --------------------- @@ -41,32 +52,30 @@ configuration to the main routing configuration file: .. code-block:: yaml - # app/config/routing.yml - app: - resource: '@AppBundle/Controller/' + # config/routes.yaml + controllers: + resource: '../src/Controller/' type: annotation This configuration will load annotations from any controller stored inside the -``src/AppBundle/Controller/`` directory and even from its subdirectories. -So if your application defines lots of controllers, it's perfectly ok to -reorganize them into subdirectories: +``src/Controller/`` directory and even from its subdirectories. So if your application +defines lots of controllers, it's perfectly ok to reorganize them into subdirectories: .. code-block:: text / ├─ ... └─ src/ - └─ AppBundle/ + ├─ ... + └─ Controller/ + ├─ DefaultController.php ├─ ... - └─ Controller/ - ├─ DefaultController.php + ├─ Api/ + │ ├─ ... + │ └─ ... + └─ Backend/ ├─ ... - ├─ Api/ - │ ├─ ... - │ └─ ... - └─ Backend/ - ├─ ... - └─ ... + └─ ... Template Configuration ---------------------- @@ -91,35 +100,35 @@ What does the Controller look like Considering all this, here is an example of what the controller should look like for the homepage of our app:: - namespace AppBundle\Controller; + namespace App\Controller; - use AppBundle\Entity\Post; - use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use App\Entity\Post; + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\Annotation\Route; - class DefaultController extends Controller + class DefaultController extends AbstractController { /** * @Route("/", name="homepage") */ - public function indexAction() + public function index() { $posts = $this->getDoctrine() ->getRepository(Post::class) ->findLatest(); - return $this->render('default/index.html.twig', array( + return $this->render('default/index.html.twig', [ 'posts' => $posts, - )); + ]); } } Fetching Services ----------------- -If you extend the base ``Controller`` class, you can access services directly from -the container via ``$this->container->get()`` or ``$this->get()``. But instead, you -should use dependency injection to fetch services: most easily done by +If you extend the base ``AbstractController`` class, you can't access services +directly from the container via ``$this->container->get()`` or ``$this->get()``. +Instead, you must use dependency injection to fetch services: most easily done by :ref:`type-hinting action method arguments `: .. best-practice:: @@ -145,44 +154,45 @@ to automatically query for an entity and pass it as an argument to your controll For example:: - use AppBundle\Entity\Post; + use App\Entity\Post; use Symfony\Component\Routing\Annotation\Route; /** * @Route("/{id}", name="admin_post_show") */ - public function showAction(Post $post) + public function show(Post $post) { $deleteForm = $this->createDeleteForm($post); - return $this->render('admin/post/show.html.twig', array( - 'post' => $post, + return $this->render('admin/post/show.html.twig', [ + 'post' => $post, 'delete_form' => $deleteForm->createView(), - )); + ]); } -Normally, you'd expect a ``$id`` argument to ``showAction()``. Instead, by -creating a new argument (``$post``) and type-hinting it with the ``Post`` -class (which is a Doctrine entity), the ParamConverter automatically queries -for an object whose ``$id`` property matches the ``{id}`` value. It will -also show a 404 page if no ``Post`` can be found. +Normally, you'd expect a ``$id`` argument to ``show()``. Instead, by creating a +new argument (``$post``) and type-hinting it with the ``Post`` class (which is a +Doctrine entity), the ParamConverter automatically queries for an object whose +``$id`` property matches the ``{id}`` value. It will also show a 404 page if no +``Post`` can be found. When Things Get More Advanced ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The above example works without any configuration because the wildcard name ``{id}`` matches -the name of the property on the entity. If this isn't true, or if you have -even more complex logic, the easiest thing to do is just query for the entity -manually. In our application, we have this situation in ``CommentController``:: +The above example works without any configuration because the wildcard name +``{id}`` matches the name of the property on the entity. If this isn't true, or +if you have even more complex logic, the easiest thing to do is just query for +the entity manually. In our application, we have this situation in +``CommentController``:: /** * @Route("/comment/{postSlug}/new", name="comment_new") */ - public function newAction(Request $request, $postSlug) + public function new(Request $request, $postSlug) { $post = $this->getDoctrine() ->getRepository(Post::class) - ->findOneBy(array('slug' => $postSlug)); + ->findOneBy(['slug' => $postSlug]); if (!$post) { throw $this->createNotFoundException(); @@ -194,7 +204,7 @@ manually. In our application, we have this situation in ``CommentController``:: You can also use the ``@ParamConverter`` configuration, which is infinitely flexible:: - use AppBundle\Entity\Post; + use App\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; @@ -203,7 +213,7 @@ flexible:: * @Route("/comment/{postSlug}/new", name="comment_new") * @ParamConverter("post", options={"mapping"={"postSlug"="slug"}}) */ - public function newAction(Request $request, Post $post) + public function new(Request $request, Post $post) { // ... } diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 9f4ac05e05f..1b5680b0607 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -4,16 +4,23 @@ Creating the Project Installing Symfony ------------------ -In the past, Symfony projects were created with `Composer`_, the dependency manager -for PHP applications. However, the current recommendation is to use the **Symfony -Installer**, which has to be installed before creating your first project. +.. best-practice:: + + Use Composer and Symfony Flex to create and manage Symfony applications. + +`Composer`_ is the package manager used by modern PHP applications to manage +their dependencies. `Symfony Flex`_ is a Composer plugin designed to automate +some of the most common tasks performed in Symfony applications. Using Flex is +optional but recommended because it improves your productivity significantly. .. best-practice:: - Use the Symfony Installer to create new Symfony-based projects. + Use the Symfony Skeleton to create new Symfony-based projects. -Read the :doc:`/setup` article learn how to install and use the Symfony -Installer. +The `Symfony Skeleton`_ is a minimal and empty Symfony project which you can +base your new projects on. Unlike past Symfony versions, this skeleton installs +the absolute bare minimum amount of dependencies to make a fully working Symfony +project. Read the :doc:`/setup` article to learn more about installing Symfony. .. _linux-and-mac-os-x-systems: .. _windows-systems: @@ -21,36 +28,21 @@ Installer. Creating the Blog Application ----------------------------- -Now that everything is correctly set up, you can create a new project based on -Symfony. In your command console, browse to a directory where you have permission -to create files and execute the following commands: +In your command console, browse to a directory where you have permission to +create files and execute the following commands: .. code-block:: terminal $ cd projects/ - $ symfony new blog - - # Windows - c:\> cd projects/ - c:\projects\> php symfony new blog - -.. note:: - - If the installer doesn't work for you or doesn't output anything, make sure - that the `Phar extension`_ is installed and enabled on your computer. + $ composer create-project symfony/skeleton blog This command creates a new directory called ``blog`` that contains a fresh new -project based on the most recent stable Symfony version available. In addition, -the installer checks if your system meets the technical requirements to execute -Symfony applications. If not, you'll see the list of changes needed to meet those -requirements. +project based on the most recent stable Symfony version available. .. tip:: - Symfony releases are digitally signed for security reasons. If you want to - verify the integrity of your Symfony installation, take a look at the - `public checksums repository`_ and follow `these steps`_ to verify the - signatures. + The technical requirements to run Symfony are simple. If you want to check + if your system meets those requirements, read :doc:`/reference/requirements`. Structuring the Application --------------------------- @@ -61,47 +53,29 @@ number of files and directories generated automatically: .. code-block:: text blog/ - ├─ app/ - │ ├─ config/ - │ └─ Resources/ - ├─ bin + ├─ bin/ │ └─ console + ├─ config/ + └─ public/ + │ └─ index.php ├─ src/ - │ └─ AppBundle/ + │ └─ Kernel.php ├─ var/ │ ├─ cache/ - │ ├─ logs/ - │ └─ sessions/ - ├─ tests/ - │ └─ AppBundle/ - ├─ vendor/ - └─ web/ + │ └─ log/ + └─ vendor/ This file and directory hierarchy is the convention proposed by Symfony to -structure your applications. The recommended purpose of each directory is the -following: - -* ``app/config/``, stores all the configuration defined for any environment; -* ``app/Resources/``, stores all the templates and the translation files for the - application; -* ``src/AppBundle/``, stores the Symfony specific code (controllers and routes), - your domain code (e.g. Doctrine classes) and all your business logic; -* ``var/cache/``, stores all the cache files generated by the application; -* ``var/logs/``, stores all the log files generated by the application; -* ``var/sessions/``, stores all the session files generated by the application; -* ``tests/AppBundle/``, stores the automatic tests (e.g. Unit tests) of the - application. -* ``vendor/``, this is the directory where Composer installs the application's - dependencies and you should never modify any of its contents; -* ``web/``, stores all the front controller files and all the web assets, such - as stylesheets, JavaScript files and images. +structure your applications. It's recommended to keep this structure because it's +easy to navigate and most directory names are self-explanatory, but you can +:doc:`override the location of any Symfony directory `: Application Bundles ~~~~~~~~~~~~~~~~~~~ When Symfony 2.0 was released, most developers naturally adopted the symfony 1.x way of dividing applications into logical modules. That's why many Symfony -apps use bundles to divide their code into logical features: UserBundle, +apps used bundles to divide their code into logical features: UserBundle, ProductBundle, InvoiceBundle, etc. But a bundle is *meant* to be something that can be reused as a stand-alone @@ -111,68 +85,16 @@ ProductBundle, then there's no advantage to having two separate bundles. .. best-practice:: - Create only one bundle called AppBundle for your application logic. - -Implementing a single AppBundle bundle in your projects will make your code -more concise and easier to understand. - -.. note:: - - There is no need to prefix the AppBundle with your own vendor (e.g. - AcmeAppBundle), because this application bundle is never going to be - shared. - -.. note:: - - Another reason to create a new bundle is when you're overriding something - in a vendor's bundle (e.g. a controller). See :doc:`/bundles/inheritance`. - -All in all, this is the typical directory structure of a Symfony application -that follows these best practices: - -.. code-block:: text - - blog/ - ├─ app/ - │ ├─ config/ - │ └─ Resources/ - ├─ bin/ - │ └─ console - ├─ src/ - │ └─ AppBundle/ - ├─ tests/ - │ └─ AppBundle/ - ├─ var/ - │ ├─ cache/ - │ ├─ logs/ - └─ sessions/ - ├─ vendor/ - └─ web/ - ├─ app.php - └─ app_dev.php - -.. tip:: - - If your Symfony installation doesn't come with a pre-generated AppBundle, - you can generate it by hand executing this command: - - .. code-block:: terminal - - $ php bin/console generate:bundle --namespace=AppBundle --dir=src --format=annotation --no-interaction - -Extending the Directory Structure ---------------------------------- + Don't create any bundle to organize your application logic. -If your project or infrastructure requires some changes to the default directory -structure of Symfony, you can -:doc:`override the location of the main directories `: -``cache/``, ``logs/`` and ``web/``. +Symfony applications can still use third-party bundles (installed in ``vendor/``) +to add features, but you should use PHP namespaces instead of bundles to organize +your own code. ---- Next: :doc:`/best_practices/configuration` .. _`Composer`: https://getcomposer.org/ -.. _`Phar extension`: https://php.net/manual/en/intro.phar.php -.. _`public checksums repository`: https://github.com/sensiolabs/checksums -.. _`these steps`: http://fabien.potencier.org/signing-project-releases.html +.. _`Symfony Flex`: https://github.com/symfony/flex +.. _`Symfony Skeleton`: https://github.com/symfony/skeleton diff --git a/best_practices/forms.rst b/best_practices/forms.rst index c51459bace0..35be1f894e7 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -12,14 +12,14 @@ Building Forms Define your forms as PHP classes. -The Form component allows you to build forms right inside your controller -code. This is perfectly fine if you don't need to reuse the form somewhere else. -But for organization and reuse, we recommend that you define each -form in its own PHP class:: +The Form component allows you to build forms right inside your controller code. +This is perfectly fine if you don't need to reuse the form somewhere else. But +for organization and reuse, we recommend that you define each form in its own +PHP class:: - namespace AppBundle\Form; + namespace App\Form; - use AppBundle\Entity\Post; + use App\Entity\Post; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -42,24 +42,24 @@ form in its own PHP class:: public function configureOptions(OptionsResolver $resolver) { - $resolver->setDefaults(array( + $resolver->setDefaults([ 'data_class' => Post::class, - )); + ]); } } .. best-practice:: - Put the form type classes in the ``AppBundle\Form`` namespace, unless you + Put the form type classes in the ``App\Form`` namespace, unless you use other custom form classes like data transformers. To use the class, use ``createForm()`` and pass the fully qualified class name:: // ... - use AppBundle\Form\PostType; + use App\Form\PostType; // ... - public function newAction(Request $request) + public function new(Request $request) { $post = new Post(); $form = $this->createForm(PostType::class, $post); @@ -67,14 +67,6 @@ To use the class, use ``createForm()`` and pass the fully qualified class name:: // ... } -Registering Forms as Services -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can also :ref:`register your form type as a service `. -This is only needed if your form type requires some dependencies to be injected -by the container, otherwise it is unnecessary overhead and therefore *not* -recommended to do this for all form type classes. - Form Button Configuration ------------------------- @@ -96,7 +88,7 @@ scope of that form:: { $builder // ... - ->add('save', SubmitType::class, array('label' => 'Create Post')) + ->add('save', SubmitType::class, ['label' => 'Create Post']) ; } @@ -107,26 +99,26 @@ This form *may* have been designed for creating posts, but if you wanted to reuse it for editing posts, the button label would be wrong. Instead, some developers configure form buttons in the controller:: - namespace AppBundle\Controller\Admin; + namespace App\Controller\Admin; + use App\Entity\Post; + use App\Form\PostType; use Symfony\Component\HttpFoundation\Request; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Form\Extension\Core\Type\SubmitType; - use AppBundle\Entity\Post; - use AppBundle\Form\PostType; class PostController extends Controller { // ... - public function newAction(Request $request) + public function new(Request $request) { $post = new Post(); $form = $this->createForm(PostType::class, $post); - $form->add('submit', SubmitType::class, array( + $form->add('submit', SubmitType::class, [ 'label' => 'Create', - 'attr' => array('class' => 'btn btn-default pull-right'), - )); + 'attr' => ['class' => 'btn btn-default pull-right'], + ]); // ... } @@ -142,8 +134,7 @@ view layer: {{ form_start(form) }} {{ form_widget(form) }} - + {{ form_end(form) }} Rendering the Form @@ -159,7 +150,7 @@ all of the fields: .. code-block:: html+twig - {{ form_start(form, {'attr': {'class': 'my-form-class'} }) }} + {{ form_start(form, {attr: {class: 'my-form-class'} }) }} {{ form_widget(form) }} {{ form_end(form) }} @@ -173,7 +164,7 @@ Handling Form Submits Handling a form submit usually follows a similar template:: - public function newAction(Request $request) + public function new(Request $request) { // build the form ... @@ -184,26 +175,18 @@ Handling a form submit usually follows a similar template:: $entityManager->persist($post); $entityManager->flush(); - return $this->redirect($this->generateUrl( - 'admin_post_show', - array('id' => $post->getId()) - )); + return $this->redirectToRoute('admin_post_show', [ + 'id' => $post->getId() + ]); } // render the template } -There are really only two notable things here. First, we recommend that you -use a single action for both rendering the form and handling the form submit. -For example, you *could* have a ``newAction()`` that *only* renders the form -and a ``createAction()`` that *only* processes the form submit. Both those -actions will be almost identical. So it's much simpler to let ``newAction()`` -handle everything. - -Second, is it required to call ``$form->isSubmitted()`` in the ``if`` statement -before calling ``isValid()``. Calling ``isValid()`` with an unsubmitted form -is deprecated since version 3.2 and will throw an exception in 4.0. - ----- +We recommend that you use a single action for both rendering the form and +handling the form submit. For example, you *could* have a ``new()`` action that +*only* renders the form and a ``create()`` action that *only* processes the form +submit. Both those actions will be almost identical. So it's much simpler to let +``new()`` handle everything. Next: :doc:`/best_practices/i18n` diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst index 06bec2b8cbb..ecb039ae19f 100644 --- a/best_practices/i18n.rst +++ b/best_practices/i18n.rst @@ -3,20 +3,18 @@ Internationalization Internationalization and localization adapt the applications and their contents to the specific region or language of the users. In Symfony this is an opt-in -feature that needs to be enabled before using it. To do this, uncomment the -following ``translator`` configuration option and set your application locale: +feature that needs to be installed before using it (``composer require translation``). -.. code-block:: yaml +Translation Source File Location +-------------------------------- + +.. best-practice:: - # app/config/config.yml - framework: - # ... - translator: { fallbacks: ['%locale%'] } + Store the translation files in the ``translations/`` directory at the root + of your project. - # app/config/parameters.yml - parameters: - # ... - locale: en +Your translators' lives will be much easier if all the application translations +are in one central location. Translation Source File Format ------------------------------ @@ -41,21 +39,6 @@ XLIFF notes allow you to define that context. The `PHP Translation Bundle`_ includes advanced extractors that can read your project and automatically update the XLIFF files. -Translation Source File Location --------------------------------- - -.. best-practice:: - - Store the translation files in the ``app/Resources/translations/`` - directory. - -Traditionally, Symfony developers have created these files in the -``Resources/translations/`` directory of each bundle. But since the -``app/Resources/`` directory is considered the global location for the -application's resources, storing translations in ``app/Resources/translations/`` -centralizes them *and* gives them priority over any other translation file. -This let's you override translations defined in third-party bundles. - Translation Keys ---------------- @@ -63,8 +46,8 @@ Translation Keys Always use keys for translations instead of content strings. -Using keys simplifies the management of the translation files because you -can change the original contents without having to update all of the translation +Using keys simplifies the management of the translation files because you can +change the original contents without having to update all of the translation files. Keys should always describe their *purpose* and *not* their location. For @@ -79,7 +62,7 @@ English in the application would be: .. code-block:: xml - + diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index dd0dc80739b..034af14ff67 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -58,8 +58,8 @@ Who this Book Is for (Hint: It's not a Tutorial) Any Symfony developer, whether you are an expert or a newcomer, can read this guide. But since this isn't a tutorial, you'll need some basic knowledge of -Symfony to follow everything. If you are totally new to Symfony, welcome! -Start with :doc:`The Quick Tour ` tutorial first. +Symfony to follow everything. If you are totally new to Symfony, welcome! and +read the :doc:`Getting Started guides ` first. We've deliberately kept this guide short. We won't repeat explanations that you can find in the vast Symfony documentation, like discussions about Dependency @@ -69,14 +69,13 @@ what you already know. The Application --------------- -In addition to this guide, a sample application has been developed with all these -best practices in mind. This project, called the Symfony Demo application, can -be obtained through the Symfony Installer. First, `download and install`_ the -installer and then execute this command to download the demo application: +In addition to this guide, a sample application called `Symfony Demo`_ has been +developed with all these best practices in mind. Execute this command to download +the demo application: .. code-block:: terminal - $ symfony demo + $ composer create-project symfony/symfony-demo **The demo application is a simple blog engine**, because that will allow us to focus on the Symfony concepts and features without getting buried in difficult @@ -87,9 +86,10 @@ Don't Update Your Existing Applications --------------------------------------- After reading this handbook, some of you may be considering refactoring your -existing Symfony applications. Our recommendation is sound and clear: **you -should not refactor your existing applications to comply with these best -practices**. The reasons for not doing it are various: +existing Symfony applications. Our recommendation is sound and clear: you may +use these best practices for **new applications** but **you should not refactor +your existing applications to comply with these best practices**. The reasons +for not doing it are various: * Your existing applications are not wrong, they just follow another set of guidelines; @@ -103,4 +103,4 @@ practices**. The reasons for not doing it are various: Next: :doc:`/best_practices/creating-the-project` .. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot -.. _`download and install`: https://symfony.com/download +.. _`Symfony Demo`: https://github.com/symfony/demo diff --git a/best_practices/security.rst b/best_practices/security.rst index 3e18057d02d..a78f8327196 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -6,10 +6,9 @@ Authentication and Firewalls (i.e. Getting the User's Credentials) You can configure Symfony to authenticate your users using any method you want and to load user information from any source. This is a complex topic, but -the :doc:`Security guide` has a lot of information about -this. +the :doc:`Security guide ` has a lot of information about this. -Regardless of your needs, authentication is configured in ``security.yml``, +Regardless of your needs, authentication is configured in ``security.yaml``, primarily under the ``firewalls`` key. .. best-practice:: @@ -30,9 +29,9 @@ site (or maybe nearly *all* sections), use the ``access_control`` area. .. best-practice:: - Use the ``bcrypt`` encoder for encoding your users' passwords. + Use the ``bcrypt`` encoder for hashing your users' passwords. -If your users have a password, then we recommend encoding it using the ``bcrypt`` +If your users have a password, then we recommend hashing it using the ``bcrypt`` encoder, instead of the traditional SHA-512 hashing encoder. The main advantages of ``bcrypt`` are the inclusion of a *salt* value to protect against rainbow table attacks, and its adaptive nature, which allows to make it slower to @@ -50,14 +49,14 @@ which uses a login form to load users from the database: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: encoders: - AppBundle\Entity\User: bcrypt + App\Entity\User: bcrypt providers: database_users: - entity: { class: AppBundle:User, property: username } + entity: { class: App\Entity\User, property: username } firewalls: secured_area: @@ -81,7 +80,7 @@ Authorization (i.e. Denying Access) ----------------------------------- Symfony gives you several ways to enforce authorization, including the ``access_control`` -configuration in :doc:`security.yml `, the +configuration in :doc:`security.yaml `, the :ref:`@Security annotation ` and using :ref:`isGranted ` on the ``security.authorization_checker`` service directly. @@ -90,17 +89,15 @@ service directly. * For protecting broad URL patterns, use ``access_control``; * Whenever possible, use the ``@Security`` annotation; - * Check security directly on the ``security.authorization_checker`` service whenever - you have a more complex situation. + * Check security directly on the ``security.authorization_checker`` service + whenever you have a more complex situation. There are also different ways to centralize your authorization logic, like -with a custom security voter or with ACL. +with a custom security voter: .. best-practice:: - * For fine-grained restrictions, define a custom security voter; - * For restricting access to *any* object by *any* user via an admin - interface, use the Symfony ACL. + Define a custom security voter to implement fine-grained restrictions. .. _best-practices-security-annotation: @@ -126,7 +123,7 @@ Using ``@Security``, this looks like: * @Route("/new", name="admin_post_new") * @Security("has_role('ROLE_ADMIN')") */ - public function newAction() + public function new() { // ... } @@ -139,7 +136,7 @@ inside ``@Security``. In the following example, a user can only access the controller if their email matches the value returned by the ``getAuthorEmail()`` method on the ``Post`` object:: - use AppBundle\Entity\Post; + use App\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Routing\Annotation\Route; @@ -147,7 +144,7 @@ method on the ``Post`` object:: * @Route("/{id}/edit", name="admin_post_edit") * @Security("user.getEmail() == post.getAuthorEmail()") */ - public function editAction(Post $post) + public function edit(Post $post) { // ... } @@ -170,7 +167,7 @@ need to repeat the expression code using Twig syntax: The easiest solution - if your logic is simple enough - is to add a new method to the ``Post`` entity that checks if a given user is its author:: - // src/AppBundle/Entity/Post.php + // src/Entity/Post.php // ... class Post @@ -190,7 +187,7 @@ to the ``Post`` entity that checks if a given user is its author:: Now you can reuse this method both in the template and in the security expression:: - use AppBundle\Entity\Post; + use App\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; use Symfony\Component\Routing\Annotation\Route; @@ -198,7 +195,7 @@ Now you can reuse this method both in the template and in the security expressio * @Route("/{id}/edit", name="admin_post_edit") * @Security("post.isAuthor(user)") */ - public function editAction(Post $post) + public function edit(Post $post) { // ... } @@ -224,7 +221,7 @@ more advanced use-case, you can always do the same security check in PHP:: /** * @Route("/{id}/edit", name="admin_post_edit") */ - public function editAction($id) + public function edit($id) { $post = $this->getDoctrine() ->getRepository(Post::class) @@ -240,42 +237,47 @@ more advanced use-case, you can always do the same security check in PHP:: // equivalent code without using the "denyAccessUnlessGranted()" shortcut: // // use Symfony\Component\Security\Core\Exception\AccessDeniedException; + // use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface + // + // ... + // + // public function __construct(AuthorizationCheckerInterface $authorizationChecker) { + // $this->authorizationChecker = $authorizationChecker; + // } + // // ... // - // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) { + // if (!$this->authorizationChecker->isGranted('edit', $post)) { // throw $this->createAccessDeniedException(); // } - + // // ... } Security Voters --------------- -If your security logic is complex and can't be centralized into a method -like ``isAuthor()``, you should leverage custom voters. These are an order -of magnitude easier than :doc:`ACLs ` and will give -you the flexibility you need in almost all cases. +If your security logic is complex and can't be centralized into a method like +``isAuthor()``, you should leverage custom voters. These are much easier than +:doc:`ACLs ` and will give you the flexibility you need in almost +all cases. First, create a voter class. The following example shows a voter that implements the same ``getAuthorEmail()`` logic you used above:: - namespace AppBundle\Security; + namespace App\Security; + use App\Entity\Post; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\User\UserInterface; - use AppBundle\Entity\Post; class PostVoter extends Voter { const CREATE = 'create'; const EDIT = 'edit'; - /** - * @var AccessDecisionManagerInterface - */ private $decisionManager; public function __construct(AccessDecisionManagerInterface $decisionManager) @@ -285,7 +287,7 @@ the same ``getAuthorEmail()`` logic you used above:: protected function supports($attribute, $subject) { - if (!in_array($attribute, array(self::CREATE, self::EDIT))) { + if (!in_array($attribute, [self::CREATE, self::EDIT])) { return false; } @@ -307,15 +309,16 @@ the same ``getAuthorEmail()`` logic you used above:: } switch ($attribute) { + // if the user is an admin, allow them to create new posts case self::CREATE: - // if the user is an admin, allow them to create new posts - if ($this->decisionManager->decide($token, array('ROLE_ADMIN'))) { + if ($this->decisionManager->decide($token, ['ROLE_ADMIN'])) { return true; } break; + + // if the user is the author of the post, allow them to edit the posts case self::EDIT: - // if the user is the author of the post, allow them to edit the posts if ($user->getEmail() === $post->getAuthorEmail()) { return true; } @@ -327,7 +330,7 @@ the same ``getAuthorEmail()`` logic you used above:: } } -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, your application will :ref:`autoconfigure ` your security voter and inject an ``AccessDecisionManagerInterface`` instance into it thanks to :doc:`autowiring `. @@ -338,7 +341,7 @@ Now, you can use the voter with the ``@Security`` annotation:: * @Route("/{id}/edit", name="admin_post_edit") * @Security("is_granted('edit', post)") */ - public function editAction(Post $post) + public function edit(Post $post) { // ... } @@ -349,20 +352,28 @@ via the even easier shortcut in a controller:: /** * @Route("/{id}/edit", name="admin_post_edit") */ - public function editAction($id) + public function edit($id) { $post = ...; // query for the post $this->denyAccessUnlessGranted('edit', $post); - // or without the shortcut: - // // use Symfony\Component\Security\Core\Exception\AccessDeniedException; + // use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface + // // ... // - // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) { + // public function __construct(AuthorizationCheckerInterface $authorizationChecker) { + // $this->authorizationChecker = $authorizationChecker; + // } + // + // ... + // + // if (!$this->authorizationChecker->isGranted('edit', $post)) { // throw $this->createAccessDeniedException(); // } + // + // ... } Learn More diff --git a/best_practices/templates.rst b/best_practices/templates.rst index 8c5b0e53ea6..1fa1fcdcfc5 100644 --- a/best_practices/templates.rst +++ b/best_practices/templates.rst @@ -9,7 +9,7 @@ languages - like `Twig`_ - were created to make templating even better. Use Twig templating format for your templates. -Generally speaking, PHP templates are much more verbose than Twig templates because +Generally speaking, PHP templates are more verbose than Twig templates because they lack native support for lots of modern features needed by templates, like inheritance, automatic escaping and named arguments for filters and functions. @@ -18,41 +18,27 @@ Twig is the default templating format in Symfony and has the largest community support of all non-PHP template engines (it's used in high profile projects such as Drupal 8). -In addition, Twig is the only template format with guaranteed support in Symfony -3.0. As a matter of fact, PHP may be removed from the officially supported -template engines. - Template Locations ------------------ .. best-practice:: - Store all your application's templates in ``app/Resources/views/`` directory. - -Traditionally, Symfony developers stored the application templates in the -``Resources/views/`` directory of each bundle. Then they used the Twig namespaced -path to refer to them (e.g. ``@AcmeDemo/Default/index.html.twig``). - -But for the templates used in your application, it's much more convenient -to store them in the ``app/Resources/views/`` directory. For starters, this -drastically simplifies their logical names: - -============================================ ================================== -Templates Stored inside Bundles Templates Stored in ``app/`` -============================================ ================================== -``@AcmeDemo/index.html.twig`` ``index.html.twig`` -``@AcmeDemo/Default/index.html.twig`` ``default/index.html.twig`` -``@AcmeDemo/Default/subdir/index.html.twig`` ``default/subdir/index.html.twig`` -============================================ ================================== + Store the application templates in the ``templates/`` directory at the root + of your project. -Another advantage is that centralizing your templates simplifies the work -of your designers. They don't need to look for templates in lots of directories -scattered through lots of bundles. +Centralizing your templates in a single location simplifies the work of your +designers. In addition, using this directory simplifies the notation used when +referring to templates (e.g. ``$this->render('admin/post/show.html.twig')`` +instead of ``$this->render('@SomeTwigNamespace/Admin/Posts/show.html.twig')``). .. best-practice:: Use lowercased snake_case for directory and template names. +This recommendation aligns with Twig best practices, where variables and template +names use lowercased snake_case too (e.g. ``user_profile`` instead of ``userProfile`` +and ``edit_form.html.twig`` instead of ``EditForm.html.twig``). + .. best-practice:: Use a prefixed underscore for partial templates in template names. @@ -67,49 +53,37 @@ Twig Extensions .. best-practice:: - Define your Twig extensions in the ``AppBundle/Twig/`` directory. Your + Define your Twig extensions in the ``src/Twig/`` directory. Your application will automatically detect them and configure them. Our application needs a custom ``md2html`` Twig filter so that we can transform -the Markdown contents of each post into HTML. - -To do this, first, install the excellent `Parsedown`_ Markdown parser as -a new dependency of the project: - -.. code-block:: terminal - - $ composer require erusev/parsedown +the Markdown contents of each post into HTML. To do this, create a new +``Markdown`` class that will be used later by the Twig extension. It just needs +to define one single method to transform Markdown content into HTML:: -Then, create a new ``Markdown`` class that will be used later by the Twig -extension. It just needs to define one single method to transform -Markdown content into HTML:: - - namespace AppBundle\Utils; + namespace App\Utils; class Markdown { - private $parser; - - public function __construct() - { - $this->parser = new \Parsedown(); - } + // ... - public function toHtml($text) + public function toHtml(string $text): string { return $this->parser->text($text); } } -Next, create a new Twig extension and define a new filter called ``md2html`` -using the ``Twig_SimpleFilter`` class. Inject the newly defined ``Markdown`` -class in the constructor of the Twig extension:: +Next, create a new Twig extension and define a filter called ``md2html`` using +the ``TwigFilter`` class. Inject the newly defined ``Markdown`` class in the +constructor of the Twig extension:: - namespace AppBundle\Twig; + namespace App\Twig; - use AppBundle\Utils\Markdown; + use App\Utils\Markdown; + use Twig\Extension\AbstractExtension; + use Twig\TwigFilter; - class AppExtension extends \Twig_Extension + class AppExtension extends AbstractExtension { private $parser; @@ -120,29 +94,23 @@ class in the constructor of the Twig extension:: public function getFilters() { - return array( - new \Twig_SimpleFilter( - 'md2html', - array($this, 'markdownToHtml'), - array('is_safe' => array('html'), 'pre_escape' => 'html') - ), - ); + return [ + new TwigFilter('md2html', [$this, 'markdownToHtml'], [ + 'is_safe' => ['html'], + 'pre_escape' => 'html', + ]), + ]; } public function markdownToHtml($content) { return $this->parser->toHtml($content); } - - public function getName() - { - return 'app_extension'; - } } And that's it! -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, you're done! Symfony will automatically know about your new service and tag it to be used as a Twig extension. diff --git a/best_practices/tests.rst b/best_practices/tests.rst index 8111ed2a8f2..5195a62c595 100644 --- a/best_practices/tests.rst +++ b/best_practices/tests.rst @@ -1,10 +1,11 @@ Tests ===== -Roughly speaking, there are two types of test. Unit testing allows you to -test the input and output of specific functions. Functional testing allows -you to command a "browser" where you browse to pages on your site, click -links, fill out forms and assert that you see certain things on the page. +Of all the different types of test available, these best practices focus solely +on unit and functional tests. Unit testing allows you to test the input and +output of specific functions. Functional testing allows you to command a +"browser" where you browse to pages on your site, click links, fill out forms +and assert that you see certain things on the page. Unit Tests ---------- @@ -28,8 +29,8 @@ functional tests, you can quickly spot any big errors before you deploy them: A functional test can be as easy as this:: - // tests/AppBundle/ApplicationAvailabilityFunctionalTest.php - namespace Tests\AppBundle; + // tests/ApplicationAvailabilityFunctionalTest.php + namespace App\Tests; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; @@ -48,14 +49,12 @@ A functional test can be as easy as this:: public function urlProvider() { - return array( - array('/'), - array('/posts'), - array('/post/fixture-post-1'), - array('/blog/category/fixture-category'), - array('/archives'), - // ... - ); + yield ['/']; + yield ['/posts']; + yield ['/post/fixture-post-1']; + yield ['/blog/category/fixture-category']; + yield ['/archives']; + // ... } } @@ -82,10 +81,13 @@ generator service: Consider the following functional test that uses the ``router`` service to generate the URL of the tested page:: + // ... + private $router; // consider that this holds the Symfony router service + public function testBlogArchives() { $client = self::createClient(); - $url = $client->getContainer()->get('router')->generate('blog_archives'); + $url = $this->router->generate('blog_archives'); $client->request('GET', $url); // ... @@ -103,7 +105,7 @@ The built-in functional testing client is great, but it can't be used to test any JavaScript behavior on your pages. If you need to test this, consider using the `Mink`_ library from within PHPUnit. -Of course, if you have a heavy JavaScript frontend, you should consider using +Of course, if you have a heavy JavaScript front-end, you should consider using pure JavaScript-based testing tools. Learn More about Functional Tests diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst index 6bed1e11086..271a1fa3eeb 100644 --- a/best_practices/web-assets.rst +++ b/best_practices/web-assets.rst @@ -2,101 +2,33 @@ Web Assets ========== Web assets are things like CSS, JavaScript and image files that make the -frontend of your site look and work great. Symfony developers have traditionally -stored these assets in the ``Resources/public/`` directory of each bundle. +frontend of your site look and work great. .. best-practice:: - Store your assets in the ``web/`` directory. + Store your assets in the ``assets/`` directory at the root of your project. -Scattering your web assets across tens of different bundles makes it more -difficult to manage them. Your designers' lives will be much easier if all -the application assets are in one location. - -Templates also benefit from centralizing your assets, because the links are -much more concise: - -.. code-block:: html+twig - - - - - {# ... #} - - - - -.. note:: - - Keep in mind that ``web/`` is a public directory and that anything stored - here will be publicly accessible, including all the original asset files - (e.g. Sass, LESS and CoffeeScript files). - -Using Assetic -------------- - -.. include:: /assetic/_standard_edition_warning.rst.inc - -These days, you probably can't simply create static CSS and JavaScript files -and include them in your template. Instead, you'll probably want to combine -and minify these to improve client-side performance. You may also want to -use LESS or Sass (for example), which means you'll need some way to process -these into CSS files. - -A lot of tools exist to solve these problems, including pure-frontend (non-PHP) -tools like GruntJS. +Your designers' and front-end developers' lives will be much easier if all the +application assets are in one central location. .. best-practice:: - Use Assetic to compile, combine and minimize web assets, unless you're - comfortable with frontend tools like GruntJS. - -:doc:`Assetic ` is an asset manager capable -of compiling assets developed with a lot of different frontend technologies -like LESS, Sass and CoffeeScript. Combining all your assets with Assetic is a -matter of wrapping all the assets with a single Twig tag: - -.. code-block:: html+twig - - {% stylesheets - 'css/bootstrap.min.css' - 'css/main.css' - filter='cssrewrite' output='css/compiled/app.css' %} - - {% endstylesheets %} - - {# ... #} - - {% javascripts - 'js/jquery.min.js' - 'js/bootstrap.min.js' - output='js/compiled/app.js' %} - - {% endjavascripts %} - -Frontend-Based Applications ---------------------------- - -Recently, frontend technologies like AngularJS have become pretty popular -for developing frontend web applications that talk to an API. - -If you are developing an application like this, you should use the tools -that are recommended by the technology, such as Bower and GruntJS. You should -develop your frontend application separately from your Symfony backend (even -separating the repositories if you want). + Use `Webpack Encore`_ to compile, combine and minimize web assets. -Learn More about Assetic ------------------------- +`Webpack`_ is the leading JavaScript module bundler that compiles, transforms +and packages assets for usage in a browser. Webpack Encore is a JavaScript +library that gets rid of most of Webpack complexity without hiding any of its +features or distorting its usage and philosophy. -Assetic can also minimize CSS and JavaScript assets -:doc:`using UglifyCSS/UglifyJS ` to speed up your -websites. You can even :doc:`compress images ` -with Assetic to reduce their size before serving them to the user. Check out -the `official Assetic documentation`_ to learn more about all the available +Webpack Encore was designed to bridge the gap between Symfony applications and +the JavaScript-based tools used in modern web applications. Check out the +`official Webpack Encore documentation`_ to learn more about all the available features. ---- Next: :doc:`/best_practices/tests` -.. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic +.. _`Webpack Encore`: https://github.com/symfony/webpack-encore +.. _`Webpack`: https://webpack.js.org/ +.. _`official Webpack Encore documentation`: https://symfony.com/doc/current/frontend.html diff --git a/bundles.rst b/bundles.rst index a4677f8a1fd..546820f5702 100644 --- a/bundles.rst +++ b/bundles.rst @@ -8,73 +8,43 @@ The Bundle System .. caution:: - In Symfony versions prior to 3.4, it was recommended to organize your own + In Symfony versions prior to 4.0, it was recommended to organize your own application code using bundles. This is no longer recommended and bundles should only be used to share code and features between multiple applications. -A bundle is similar to a plugin in other software, but even better. The key -difference is that *everything* is a bundle in Symfony, including both the -core framework functionality and the code written for your application. -Bundles are first-class citizens in Symfony. This gives you the flexibility -to use pre-built features packaged in `third-party bundles`_ or to distribute -your own bundles. It makes it easy to pick and choose which features to enable -in your application and to optimize them the way you want. - -.. note:: - - While you'll learn the basics here, an entire article is devoted to the - organization and best practices of :doc:`bundles `. - -A bundle is simply a structured set of files within a directory that implement -a single feature. You might create a BlogBundle, a ForumBundle or -a bundle for user management (many of these exist already as open source -bundles). Each directory contains everything related to that feature, including -PHP files, templates, stylesheets, JavaScript files, tests and anything else. -Every aspect of a feature exists in a bundle and every feature lives in a -bundle. - -Bundles used in your applications must be enabled by registering them in -the ``registerBundles()`` method of the ``AppKernel`` class:: - - // app/AppKernel.php - public function registerBundles() - { - $bundles = array( - new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new Symfony\Bundle\SecurityBundle\SecurityBundle(), - new Symfony\Bundle\TwigBundle\TwigBundle(), - new Symfony\Bundle\MonologBundle\MonologBundle(), - new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(), - new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), - new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), - new AppBundle\AppBundle(), - ); - - if (in_array($this->getEnvironment(), array('dev', 'test'))) { - $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); - $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); - $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); - } - - return $bundles; - } - -With the ``registerBundles()`` method, you have total control over which bundles -are used by your application (including the core Symfony bundles). +A bundle is similar to a plugin in other software, but even better. The core +features of Symfony framework are implemented with bundles (FrameworkBundle, +SecurityBundle, DebugBundle, etc.) They are also used to add new features in +your application via `third-party bundles`_. + +Bundles used in your applications must be enabled per +:doc:`environment ` in the ``config/bundles.php`` +file:: + + // config/bundles.php + return [ + // 'all' means that the bundle is enabled for any Symfony environment + Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], + Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], + Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], + // this bundle is enabled only in 'dev' and 'test', so you can't use it in 'prod' + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], + ]; .. tip:: - A bundle can live *anywhere* as long as it can be autoloaded (via the - autoloader configured at ``app/autoload.php``). + In a default Symfony application that uses :doc:`Symfony Flex `, + bundles are enabled/disabled automatically for you when installing/removing + them, so you don't need to look at or edit this ``bundles.php`` file. Creating a Bundle ----------------- -`SensioGeneratorBundle`_ is an optional bundle that includes commands to create -different elements of your application, such as bundles. If you create lots of -bundles, consider using it. However, this section creates and enables a new -bundle by hand to show how simple it is to do it. - +This section creates and enables a new bundle to show how simple it is to do it. The new bundle is called AcmeTestBundle, where the ``Acme`` portion is just a dummy name that should be replaced by some "vendor" name that represents you or your organization (e.g. ABCTestBundle for some company named ``ABC``). @@ -83,7 +53,7 @@ Start by creating a ``src/Acme/TestBundle/`` directory and adding a new file called ``AcmeTestBundle.php``:: // src/Acme/TestBundle/AcmeTestBundle.php - namespace Acme\TestBundle; + namespace App\Acme\TestBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -100,43 +70,16 @@ called ``AcmeTestBundle.php``:: This empty class is the only piece you need to create the new bundle. Though commonly empty, this class is powerful and can be used to customize the behavior -of the bundle. - -Now that you've created the bundle, enable it via the ``AppKernel`` class:: - - // app/AppKernel.php - public function registerBundles() - { - $bundles = array( - // ... +of the bundle. Now that you've created the bundle, enable it:: - // register your bundle - new Acme\TestBundle\AcmeTestBundle(), - ); + // config/bundles.php + return [ // ... - - return $bundles; - } + App\Acme\TestBundle\AcmeTestBundle::class => ['all' => true], + ]; And while it doesn't do anything yet, AcmeTestBundle is now ready to be used. -And as easy as this is, Symfony also provides a command-line interface for -generating a basic bundle skeleton: - -.. code-block:: terminal - - $ php bin/console generate:bundle --namespace=Acme/TestBundle - -The bundle skeleton generates a basic controller, template and routing -resource that can be customized. You'll learn more about Symfony's command-line -tools later. - -.. tip:: - - Whenever creating a new bundle or using a third-party bundle, always make - sure the bundle has been enabled in ``registerBundles()``. When using - the ``generate:bundle`` command, this is done for you. - Bundle Directory Structure -------------------------- @@ -154,14 +97,14 @@ of the most common elements of a bundle: necessary). ``Resources/config/`` - Houses configuration, including routing configuration (e.g. ``routing.yml``). + Houses configuration, including routing configuration (e.g. ``routing.yaml``). ``Resources/views/`` Holds templates organized by controller name (e.g. ``Random/index.html.twig``). ``Resources/public/`` Contains web assets (images, stylesheets, etc) and is copied or symbolically - linked into the project ``web/`` directory via the ``assets:install`` console + linked into the project ``public/`` directory via the ``assets:install`` console command. ``Tests/`` @@ -178,11 +121,10 @@ the bundle. Learn more ---------- -.. toctree:: - :maxdepth: 1 - :glob: - - bundles/* +* :doc:`/bundles/override` +* :doc:`/bundles/best_practices` +* :doc:`/bundles/configuration` +* :doc:`/bundles/extension` +* :doc:`/bundles/prepend_extension` .. _`third-party bundles`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories -.. _`SensioGeneratorBundle`: https://symfony.com/doc/current/bundles/SensioGeneratorBundle/index.html diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index 04e00f0ccfb..3040782e8b2 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -4,21 +4,10 @@ Best Practices for Reusable Bundles =================================== -There are two types of bundles: - -* Application-specific bundles: only used to build your application; -* Reusable bundles: meant to be shared across many projects. - This article is all about how to structure your **reusable bundles** so that -they're easy to configure and extend. Many of these recommendations do not -apply to application bundles because you'll want to keep those as simple -as possible. For application bundles, just follow the practices shown throughout -the guides. - -.. seealso:: - - The best practices for application-specific bundles are discussed in - :doc:`/best_practices/introduction`. +they're easy to configure and extend. Reusable bundles are those meant to be +shared privately across many company projects or publicly so any Symfony project +can install them. .. index:: pair: Bundle; Naming conventions @@ -28,10 +17,10 @@ the guides. Bundle Name ----------- -A bundle is also a PHP namespace. The namespace must follow the `PSR-0`_ or -`PSR-4`_ interoperability standards for PHP namespaces and class names: it starts -with a vendor segment, followed by zero or more category segments, and it ends -with the namespace short name, which must end with ``Bundle``. +A bundle is also a PHP namespace. The namespace must follow the `PSR-4`_ +interoperability standard for PHP namespaces and class names: it starts with a +vendor segment, followed by zero or more category segments, and it ends with the +namespace short name, which must end with ``Bundle``. A namespace becomes a bundle as soon as you add a bundle class to it. The bundle class name must follow these simple rules: @@ -124,8 +113,8 @@ Service Container Extensions ``DependencyInjection/`` Doctrine ORM entities (when not using annotations) ``Entity/`` Doctrine ODM documents (when not using annotations) ``Document/`` Event Listeners ``EventListener/`` -Configuration ``Resources/config/`` -Web Resources (CSS, JS, images) ``Resources/public/`` +Configuration (routes, services, etc.) ``Resources/config/`` +Web Assets (CSS, JS, images) ``Resources/public/`` Translation files ``Resources/translations/`` Validation (when not using annotations) ``Resources/config/validation/`` Serialization (when not using annotations) ``Resources/config/serialization/`` @@ -177,6 +166,92 @@ the ``Tests/`` directory. Tests should follow the following principles: A test suite must not contain ``AllTests.php`` scripts, but must rely on the existence of a ``phpunit.xml.dist`` file. +Continuous Integration +---------------------- + +Testing bundle code continuously, including all its commits and pull requests, +is a good practice called Continuous Integration. There are several services +providing this feature for free for open source projects. The most popular +service for Symfony bundles is called `Travis CI`_. + +Here is the recommended configuration file (``.travis.yml``) for Symfony bundles, +which test the two latest :doc:`LTS versions ` +of Symfony and the latest beta release: + +.. code-block:: yaml + + language: php + sudo: false + cache: + directories: + - $HOME/.composer/cache/files + - $HOME/symfony-bridge/.phpunit + + env: + global: + - PHPUNIT_FLAGS="-v" + - SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit" + + matrix: + fast_finish: true + include: + # Minimum supported dependencies with the latest and oldest PHP version + - php: 7.2 + env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors" + - php: 7.0 + env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors" + + # Test the latest stable release + - php: 7.0 + - php: 7.1 + - php: 7.2 + env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" + + # Test LTS versions. This makes sure we do not use Symfony packages with version greater + # than 2 or 3 respectively. Read more at https://github.com/symfony/lts + - php: 7.2 + env: DEPENDENCIES="symfony/lts:^2" + - php: 7.2 + env: DEPENDENCIES="symfony/lts:^3" + + # Latest commit to master + - php: 7.2 + env: STABILITY="dev" + + allow_failures: + # Dev-master is allowed to fail. + - env: STABILITY="dev" + + before_install: + - if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi + - if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi; + - if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi; + + install: + # To be removed when this issue will be resolved: https://github.com/composer/composer/issues/5355 + - if [[ "$COMPOSER_FLAGS" == *"--prefer-lowest"* ]]; then composer update --prefer-dist --no-interaction --prefer-stable --quiet; fi + - composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction + - ./vendor/bin/simple-phpunit install + + script: + - composer validate --strict --no-check-lock + # simple-phpunit is the PHPUnit wrapper provided by the PHPUnit Bridge component and + # it helps with testing legacy code and deprecations (composer require symfony/phpunit-bridge) + - ./vendor/bin/simple-phpunit $PHPUNIT_FLAGS + +Consider using `Travis cron`_ too to make sure your project is built even if +there are no new pull requests or commits. + +Installation +------------ + +Bundles should set ``"type": "symfony-bundle"`` in their ``composer.json`` file. +With this, :doc:`Symfony Flex ` will be able to automatically +enable your bundle when it's installed. + +If your bundle requires any setup (e.g. configuration, new files, changes to +`.gitignore`, etc), then you should create a `Symfony Flex recipe`_. + Documentation ------------- @@ -203,8 +278,19 @@ following standardized instructions in your ``README.md`` file. Installation ============ - Step 1: Download the Bundle - --------------------------- + Applications that use Symfony Flex + ---------------------------------- + + Open a command console, enter your project directory and execute: + + ```console + $ composer require + ``` + + Applications that don't use Symfony Flex + ---------------------------------------- + + ### Step 1: Download the Bundle Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle: @@ -217,8 +303,7 @@ following standardized instructions in your ``README.md`` file. in the [installation chapter](https://getcomposer.org/doc/00-intro.md) of the Composer documentation. - Step 2: Enable the Bundle - ------------------------- + ### Step 2: Enable the Bundle Then, enable the bundle by adding it to the list of registered bundles in the `app/AppKernel.php` file of your project: @@ -249,8 +334,20 @@ following standardized instructions in your ``README.md`` file. Installation ============ + Applications that use Symfony Flex + ---------------------------------- + + Open a command console, enter your project directory and execute: + + .. code-block:: bash + + $ composer require + + Applications that don't use Symfony Flex + ---------------------------------------- + Step 1: Download the Bundle - --------------------------- + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle: @@ -263,7 +360,7 @@ following standardized instructions in your ``README.md`` file. in the `installation chapter`_ of the Composer documentation. Step 2: Enable the Bundle - ------------------------- + ~~~~~~~~~~~~~~~~~~~~~~~~~ Then, enable the bundle by adding it to the list of registered bundles in the ``app/AppKernel.php`` file of your project: @@ -342,13 +439,13 @@ The end user can provide values in any configuration file: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml parameters: acme_blog.author.email: 'fabien@example.com' .. code-block:: xml - + setParameter('acme_blog.author.email', 'fabien@example.com'); Retrieve the configuration parameters in your code from the container:: @@ -422,9 +519,8 @@ The ``composer.json`` file should include at least the following metadata: a string (or array of strings) with a `valid license identifier`_, such as ``MIT``. ``autoload`` - This information is used by Symfony to load the classes of the bundle. The - `PSR-4`_ autoload standard is recommended for modern bundles, but `PSR-0`_ - standard is also supported. + This information is used by Symfony to load the classes of the bundle. It's + recommended to use the `PSR-4`_ autoload standard. In order to make it easier for developers to find your bundle, register it on `Packagist`_, the official repository for Composer packages. @@ -434,7 +530,7 @@ Resources If the bundle references any resources (config files, translation files, etc.), don't use physical paths (e.g. ``__DIR__/config/services.xml``) but logical -paths (e.g. ``@AppBundle/Resources/config/services.xml``). +paths (e.g. ``@FooBundle/Resources/config/services.xml``). The logical paths are required because of the bundle overriding mechanism that lets you override any resource/file of any bundle. See :ref:`http-kernel-resource-locator` @@ -442,7 +538,7 @@ for more details about transforming physical paths into logical paths. Beware that templates use a simplified version of the logical path shown above. For example, an ``index.html.twig`` template located in the ``Resources/views/Default/`` -directory of the AppBundle, is referenced as ``@App/Default/index.html.twig``. +directory of the FooBundle, is referenced as ``@Foo/Default/index.html.twig``. Learn more ---------- @@ -450,9 +546,11 @@ Learn more * :doc:`/bundles/extension` * :doc:`/bundles/configuration` -.. _`PSR-0`: https://www.php-fig.org/psr/psr-0/ .. _`PSR-4`: https://www.php-fig.org/psr/psr-4/ +.. _`Symfony Flex recipe`: https://github.com/symfony/recipes .. _`Semantic Versioning Standard`: https://semver.org/ .. _`Packagist`: https://packagist.org/ .. _`choose any license`: https://choosealicense.com/ .. _`valid license identifier`: https://spdx.org/licenses/ +.. _`Travis CI`: https://travis-ci.org/ +.. _`Travis Cron`: https://docs.travis-ci.com/user/cron-jobs/ diff --git a/bundles/configuration.rst b/bundles/configuration.rst index d155ea86022..e761a2b6f9e 100644 --- a/bundles/configuration.rst +++ b/bundles/configuration.rst @@ -5,11 +5,12 @@ How to Create Friendly Configuration for a Bundle ================================================= -If you open your application configuration file (usually ``app/config/config.yml``), -you'll see a number of different configuration sections, such as ``framework``, -``twig`` and ``doctrine``. Each of these configures a specific bundle, allowing -you to define options at a high level and then let the bundle make all the -low-level, complex changes based on your settings. +If you open your main application configuration directory (usually +``config/packages/``), you'll see a number of different files, such as +``framework.yaml``, ``twig.yaml`` and ``doctrine.yaml``. Each of these +configures a specific bundle, allowing you to define options at a high level and +then let the bundle make all the low-level, complex changes based on your +settings. For example, the following configuration tells the FrameworkBundle to enable the form integration, which involves the definition of quite a few services as well @@ -43,17 +44,6 @@ as integration of other related components: 'form' => true, )); -.. sidebar:: Using Parameters to Configure your Bundle - - If you don't have plans to share your bundle between projects, it doesn't - make sense to use this more advanced way of configuration. Since you use - the bundle only in one project, you can just change the service - configuration each time. - - If you *do* want to be able to configure something from within - ``config.yml``, you can always create a parameter there and use that - parameter somewhere else. - Using the Bundle Extension -------------------------- @@ -71,7 +61,7 @@ bundle configuration would look like: .. code-block:: yaml - # app/config/config.yml + # config/packages/acme_social.yaml acme_social: twitter: client_id: 123 @@ -79,7 +69,7 @@ bundle configuration would look like: .. code-block:: xml - + loadFromExtension('acme_social', array( 'client_id' => 123, 'client_secret' => 'your_secret', @@ -144,20 +134,20 @@ For the configuration example in the previous section, the array passed to your ) Notice that this is an *array of arrays*, not just a single flat array of the -configuration values. This is intentional, as it allows Symfony to parse -several configuration resources. For example, if ``acme_social`` appears in -another configuration file - say ``config_dev.yml`` - with different values -beneath it, the incoming array might look like this:: +configuration values. This is intentional, as it allows Symfony to parse several +configuration resources. For example, if ``acme_social`` appears in another +configuration file - say ``config/packages/dev/acme_social.yaml`` - with +different values beneath it, the incoming array might look like this:: array( - // values from config.yml + // values from config/packages/acme_social.yaml array( 'twitter' => array( 'client_id' => 123, 'client_secret' => 'your_secret', ), ), - // values from config_dev.yml + // values from config/packages/dev/acme_social.yaml array( 'twitter' => array( 'client_id' => 456, @@ -218,7 +208,6 @@ force validation (e.g. if an additional option was passed, an exception will be thrown):: // src/Acme/SocialBundle/DependencyInjection/AcmeSocialExtension.php - public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); @@ -322,12 +311,10 @@ In your extension, you can load this and dynamically set its arguments:: Modifying the Configuration of Another Bundle --------------------------------------------- -If you have multiple bundles that depend on each other, it may be useful -to allow one ``Extension`` class to modify the configuration passed to another -bundle's ``Extension`` class, as if the end-developer has actually placed that -configuration in their ``app/config/config.yml`` file. This can be achieved -using a prepend extension. For more details, see -:doc:`/bundles/prepend_extension`. +If you have multiple bundles that depend on each other, it may be useful to +allow one ``Extension`` class to modify the configuration passed to another +bundle's ``Extension`` class. This can be achieved using a prepend extension. +For more details, see :doc:`/bundles/prepend_extension`. Dump the Configuration ---------------------- @@ -399,7 +386,7 @@ namespace is then replaced with the XSD validation base path returned from method. This namespace is then followed by the rest of the path from the base path to the file itself. -By convention, the XSD file lives in the ``Resources/config/schema``, but you +By convention, the XSD file lives in the ``Resources/config/schema/``, but you can place it anywhere you like. You should return this path as the base path:: // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php @@ -420,7 +407,7 @@ Assuming the XSD file is called ``hello-1.0.xsd``, the schema location will be .. code-block:: xml - + ` -to return the correct DI alias. The DI alias is the name used to refer to the -bundle in the container (e.g. in the ``app/config/config.yml`` file). By +method to return the correct DI alias. The DI alias is the name used to refer to +the bundle in the container (e.g. in the ``config/packages/`` files). By default, this is done by removing the ``Extension`` suffix and converting the class name to underscores (e.g. ``AcmeHelloExtension``'s DI alias is ``acme_hello``). @@ -86,11 +83,10 @@ container. In the ``load()`` method, you can use PHP code to register service definitions, but it is more common if you put these definitions in a configuration file -(using the Yaml, XML or PHP format). Luckily, you can use the file loaders in -the extension! +(using the YAML, XML or PHP format). For instance, assume you have a file called ``services.xml`` in the -``Resources/config`` directory of your bundle, your ``load()`` method looks like:: +``Resources/config/`` directory of your bundle, your ``load()`` method looks like:: use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\Config\FileLocator; @@ -105,65 +101,37 @@ For instance, assume you have a file called ``services.xml`` in the $loader->load('services.xml'); } -Other available loaders are the ``YamlFileLoader``, ``PhpFileLoader`` and -``IniFileLoader``. - -.. note:: - - The ``IniFileLoader`` can only be used to load parameters and it can only - load them as strings. - -.. caution:: - - If you removed the default file with service definitions (i.e. - ``app/config/services.yml``), make sure to also remove it from the - ``imports`` key in ``app/config/config.yml``. +The other available loaders are ``YamlFileLoader`` and ``PhpFileLoader``. Using Configuration to Change the Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Extension is also the class that handles the configuration for that -particular bundle (e.g. the configuration in ``app/config/config.yml``). To -read more about it, see the ":doc:`/bundles/configuration`" article. +particular bundle (e.g. the configuration in ``config/packages/.yaml``). +To read more about it, see the ":doc:`/bundles/configuration`" article. Adding Classes to Compile ------------------------- -.. versionadded:: 3.3 - This technique is discouraged and the ``addClassesToCompile()`` method was - deprecated in Symfony 3.3 because modern PHP versions make it unnecessary. - -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. +Bundles can hint Symfony about which of their classes contain annotations so +they are compiled when generating the application cache to improve the overall +performance. Define the list of annotated classes to compile in the +``addAnnotatedClassesToCompile()`` method:: -.. versionadded:: 3.2 - The ``addAnnotatedClassesToCompile()`` method was added in Symfony 3.2. - -Your bundles can also add their own classes into this file thanks to the -``addClassesToCompile()`` and ``addAnnotatedClassesToCompile()`` methods (both -work in the same way, but the second one is for classes that contain PHP -annotations). Define the classes to compile as an array of their fully qualified -class names:: - - use AppBundle\Manager\UserManager; - use AppBundle\Utils\Slugger; + use App\Manager\UserManager; + use App\Utils\Slugger; // ... public function load(array $configs, ContainerBuilder $container) { // ... - // this method can't compile classes that contain PHP annotations - $this->addClassesToCompile(array( - UserManager::class, - Slugger::class, - // ... - )); - - // add here only classes that contain PHP annotations $this->addAnnotatedClassesToCompile(array( - 'AppBundle\\Controller\\DefaultController', + // you can define the fully qualified class names... + 'App\\Controller\\DefaultController', + // ... but glob patterns are also supported: + '**Bundle\\Controller\\', + // ... )); } @@ -173,27 +141,6 @@ class names:: If some class extends from other classes, all its parents are automatically included in the list of classes to compile. -.. versionadded:: 3.2 - The option to add classes to compile using patterns was introduced in Symfony 3.2. - -The classes to compile can also be added using file path patterns:: - - // ... - public function load(array $configs, ContainerBuilder $container) - { - // ... - - $this->addClassesToCompile(array( - '**Bundle\\Manager\\', - // ... - )); - - $this->addAnnotatedClassesToCompile(array( - '**Bundle\\Controller\\', - // ... - )); - } - Patterns are transformed into the actual class namespaces using the classmap generated by Composer. Therefore, before using these patterns, you must generate the full classmap executing the ``dump-autoload`` command of Composer. diff --git a/bundles/index.rst b/bundles/index.rst index 87641de5e23..78dd8c6d4fb 100644 --- a/bundles/index.rst +++ b/bundles/index.rst @@ -1,14 +1,14 @@ +:orphan: + Bundles ======= .. toctree:: :maxdepth: 2 - installation - best_practices - inheritance override - remove - extension + inheritance + best_practices configuration + extension prepend_extension diff --git a/bundles/inheritance.rst b/bundles/inheritance.rst index 458d1cac92a..d8ce372adb4 100644 --- a/bundles/inheritance.rst +++ b/bundles/inheritance.rst @@ -6,107 +6,6 @@ How to Use Bundle Inheritance to Override Parts of a Bundle .. caution:: - Bundle inheritance is deprecated since Symfony 3.4 and will be removed in - 4.0. - -When working with third-party bundles, you'll probably come across a situation -where you want to override a file in that third-party bundle with a file -in one of your own bundles. Symfony gives you a very convenient way to override -things like controllers, templates, and other files in a bundle's -``Resources/`` directory. - -For example, suppose that you have installed `FOSUserBundle`_, but you want to -override its base ``layout.html.twig`` template, as well as one of its -controllers. - -First, create a new bundle called UserBundle and enable it in your application. -Then, register the third-party FOSUserBundle as the "parent" of your bundle:: - - // src/UserBundle/UserBundle.php - namespace UserBundle; - - use Symfony\Component\HttpKernel\Bundle\Bundle; - - class UserBundle extends Bundle - { - public function getParent() - { - return 'FOSUserBundle'; - } - } - -By making this simple change, you can now override several parts of the FOSUserBundle -simply by creating a file with the same name. - -.. note:: - - Despite the method name, there is no parent/child relationship between - the bundles, it is just a way to extend and override an existing bundle. - -Overriding Controllers -~~~~~~~~~~~~~~~~~~~~~~ - -Suppose you want to add some functionality to the ``registerAction()`` of a -``RegistrationController`` that lives inside FOSUserBundle. To do so, -just create your own ``RegistrationController.php`` file, override the bundle's -original method, and change its functionality:: - - // src/UserBundle/Controller/RegistrationController.php - namespace UserBundle\Controller; - - use FOS\UserBundle\Controller\RegistrationController as BaseController; - - class RegistrationController extends BaseController - { - public function registerAction() - { - $response = parent::registerAction(); - - // ... do custom stuff - return $response; - } - } - -.. tip:: - - Depending on how severely you need to change the behavior, you might - call ``parent::registerAction()`` or completely replace its logic with - your own. - -.. note:: - - Overriding controllers in this way only works if the bundle refers to - the controller using the standard ``FOSUserBundle:Registration:register`` - syntax in routes and templates. This is the best practice. - -Overriding Resources: Templates, Routing, etc -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Most resources can also be overridden, simply by creating a file in the same -location as your parent bundle. - -For example, it's very common to need to override the FOSUserBundle's -``layout.html.twig`` template so that it uses your application's base layout. -Since the file lives at ``Resources/views/layout.html.twig`` in the FOSUserBundle, -you can create your own file in the same location of UserBundle. Symfony will -ignore the file that lives inside the FOSUserBundle entirely, and use your file -instead. - -The same goes for routing files and some other resources. - -.. note:: - - The overriding of resources only works when you refer to resources with - the ``@FOSUserBundle/Resources/config/routing/security.xml`` method. - You need to use the ``@BundleName`` shortcut when referring to resources - so they can be successfully overridden (except templates, which are - overridden in a different way, as explained in :doc:`/templating/overriding`). - -.. caution:: - - Translation and validation files do not work in the same way as described - above. Read ":ref:`override-translations`" if you want to learn how to - override translations and see ":ref:`override-validation`" for tricks to - override the validation. - -.. _`FOSUserBundle`: https://github.com/friendsofsymfony/fosuserbundle + Bundle inheritance was removed in Symfony 4.0, but you can + :doc:`override any part of a bundle ` without + using bundle inheritance. diff --git a/bundles/installation.rst b/bundles/installation.rst deleted file mode 100644 index b2c62b02bc3..00000000000 --- a/bundles/installation.rst +++ /dev/null @@ -1,160 +0,0 @@ -.. index:: - single: Bundle; Installation - -How to Install 3rd Party Bundles -================================ - -Most bundles provide their own installation instructions. However, the -basic steps for installing a bundle are the same: - -* `A) Add Composer Dependencies`_ -* `B) Enable the Bundle`_ -* `C) Configure the Bundle`_ - -A) Add Composer Dependencies ----------------------------- - -Dependencies are managed with Composer, so if Composer is new to you, learn -some basics in `their documentation`_. This involves two steps: - -1) Find out the Name of the Bundle on Packagist -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The README for a bundle (e.g. `FOSUserBundle`_) usually tells you its name -(e.g. ``friendsofsymfony/user-bundle``). If it doesn't, you can search for -the bundle on the `Packagist.org`_ site. - -.. tip:: - - Looking for bundles? Try searching for `symfony-bundle topic on GitHub`_. - -2) Install the Bundle via Composer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that you know the package name, you can install it via Composer: - -.. code-block:: terminal - - $ composer require friendsofsymfony/user-bundle - -This will choose the best version for your project, add it to ``composer.json`` -and download its code into the ``vendor/`` directory. If you need a specific -version, include it as the second argument of the `composer require`_ command: - -.. code-block:: terminal - - $ composer require friendsofsymfony/user-bundle "~2.0" - -B) Enable the Bundle --------------------- - -At this point, the bundle is installed in your Symfony project (e.g. -``vendor/friendsofsymfony/``) and the autoloader recognizes its classes. -The only thing you need to do now is register the bundle in ``AppKernel``:: - - // app/AppKernel.php - - // ... - class AppKernel extends Kernel - { - // ... - - public function registerBundles() - { - $bundles = array( - // ... - new FOS\UserBundle\FOSUserBundle(), - ); - - // ... - } - } - -In a few rare cases, you may want a bundle to be *only* enabled in the development -:doc:`environment `. For example, -the DoctrineFixturesBundle helps to load dummy data - something you probably -only want to do while developing. To only load this bundle in the ``dev`` -and ``test`` environments, register the bundle in this way:: - - // app/AppKernel.php - - // ... - class AppKernel extends Kernel - { - // ... - - public function registerBundles() - { - $bundles = array( - // ... - ); - - if (in_array($this->getEnvironment(), array('dev', 'test'))) { - $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(); - } - - // ... - } - } - -C) Configure the Bundle ------------------------ - -It's pretty common for a bundle to need some additional setup or configuration -in ``app/config/config.yml``. The bundle's documentation will tell you about -the configuration, but you can also get a reference of the bundle's configuration -via the ``config:dump-reference`` command: - -.. code-block:: terminal - - $ bin/console config:dump-reference AsseticBundle - -Instead of the full bundle name, you can also pass the short name used as the root -of the bundle's configuration: - -.. code-block:: terminal - - $ bin/console config:dump-reference assetic - -The output will look like this: - -.. code-block:: yaml - - assetic: - debug: '%kernel.debug%' - use_controller: - enabled: '%kernel.debug%' - profiler: false - read_from: '%kernel.project_dir%/web' - write_to: '%assetic.read_from%' - java: /usr/bin/java - node: /usr/local/bin/node - node_paths: [] - # ... - -.. tip:: - - For complex bundles that define lots of configuration options, you can pass - a second optional argument to the ``config:dump-reference`` command to only - display a section of the entire configuration: - - .. code-block:: terminal - - $ bin/console config:dump-reference AsseticBundle use_controller - - # Default configuration for "AsseticBundle" at path "use_controller" - use_controller: - enabled: '%kernel.debug%' - profiler: false - -Other Setup ------------ - -At this point, check the ``README`` file of your brand new bundle to see -what to do next. Have fun! - -.. _their documentation: https://getcomposer.org/doc/00-intro.md -.. _Packagist.org: https://packagist.org -.. _FOSUserBundle: https://github.com/FriendsOfSymfony/FOSUserBundle -.. _`composer require`: https://getcomposer.org/doc/03-cli.md#require -.. _`symfony-bundle topic on GitHub`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories diff --git a/bundles/override.rst b/bundles/override.rst index 3c0fb8c7f2d..0b4bca5f95c 100644 --- a/bundles/override.rst +++ b/bundles/override.rst @@ -4,15 +4,15 @@ How to Override any Part of a Bundle ==================================== -This document is a quick reference for how to override different parts of -third-party bundles without using :doc:`/bundles/inheritance`, which is -deprecated since Symfony 3.4. +When using a third-party bundle, you might want to customize or override some of +its features. This document describes ways of overriding the most common +features of a bundle. .. tip:: The bundle overriding mechanism means that you cannot use physical paths to refer to bundle's resources (e.g. ``__DIR__/config/services.xml``). Always - use logical paths in your bundles (e.g. ``@AppBundle/Resources/config/services.xml``) + use logical paths in your bundles (e.g. ``@FooBundle/Resources/config/services.xml``) and call the :ref:`locateResource() method ` to turn them into physical paths when needed. @@ -26,7 +26,7 @@ Routing Routing is never automatically imported in Symfony. If you want to include the routes from any bundle, then they must be manually imported from somewhere -in your application (e.g. ``app/config/routing.yml``). +in your application (e.g. ``config/routes.yaml``). The easiest way to "override" a bundle's routing is to never import it at all. Instead of importing a third-party bundle's routing, simply copy @@ -46,22 +46,25 @@ Services & Configuration If you want to modify service definitions of another bundle, you can use a compiler pass to change the class of the service or to modify method calls. In the following example, the implementing class for the ``original-service-id`` is changed to -``Acme\DemoBundle\YourService``:: +``App\YourService``: - // src/Acme/DemoBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php - namespace Acme\DemoBundle\DependencyInjection\Compiler; +.. code-block:: diff - use Acme\DemoBundle\YourService; - use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; - use Symfony\Component\DependencyInjection\ContainerBuilder; + // src/Kernel.php + namespace App; - class OverrideServiceCompilerPass implements CompilerPassInterface + // ... + + use App\Service\YourService; + + use Symfony\Component\DependencyInjection\ContainerBuilder; + + use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + + class Kernel extends BaseKernel implements CompilerPassInterface { - public function process(ContainerBuilder $container) - { - $definition = $container->getDefinition('original-service-id'); - $definition->setClass(YourService::class); - } + + public function process(ContainerBuilder $container) + + { + + $definition = $container->findDefinition('original-service-id'); + + $definition->setClass(YourService::class); + + } } For more information on compiler passes, see :doc:`/service_container/compiler_passes`. @@ -99,7 +102,7 @@ to a new validation group: .. code-block:: yaml - # src/Acme/UserBundle/Resources/config/validation.yml + # config/validator/validation.yaml FOS\UserBundle\Model\User: properties: plainPassword: @@ -112,7 +115,7 @@ to a new validation group: .. code-block:: xml - + prependExtensionConfig($name, $config); break; @@ -96,14 +95,15 @@ in case a specific other bundle is not registered:: } The above would be the equivalent of writing the following into the -``app/config/config.yml`` in case AcmeGoodbyeBundle is not registered and the -``entity_manager_name`` setting for ``acme_hello`` is set to ``non_default``: +``config/packages/acme_something.yaml`` in case AcmeGoodbyeBundle is not +registered and the ``entity_manager_name`` setting for ``acme_hello`` is set to +``non_default``: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/packages/acme_something.yaml acme_something: # ... use_acme_goodbye: false @@ -115,7 +115,7 @@ The above would be the equivalent of writing the following into the .. code-block:: xml - + loadFromExtension('acme_something', array( // ... 'use_acme_goodbye' => false, diff --git a/bundles/remove.rst b/bundles/remove.rst deleted file mode 100644 index 644e8742310..00000000000 --- a/bundles/remove.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. index:: - single: Bundle; Removing a bundle - -How to Remove a Bundle -====================== - -1. Unregister the Bundle in the ``AppKernel`` ---------------------------------------------- - -To disconnect the bundle from the framework, you should remove the bundle from -the ``AppKernel::registerBundles()`` method. The bundle will likely be found in -the ``$bundles`` array declaration or added to it in a later statement if the -bundle is only registered in the development environment:: - - // app/AppKernel.php - - // ... - class AppKernel extends Kernel - { - public function registerBundles() - { - $bundles = array( - new Acme\DemoBundle\AcmeDemoBundle(), - ); - - if (in_array($this->getEnvironment(), array('dev', 'test'))) { - // comment or remove this line: - // $bundles[] = new Acme\DemoBundle\AcmeDemoBundle(); - // ... - } - } - } - -2. Remove Bundle Configuration ------------------------------- - -Now that Symfony doesn't know about the bundle, you need to remove any -configuration and routing configuration inside the ``app/config`` directory -that refers to the bundle. - -2.1 Remove Bundle Routing -~~~~~~~~~~~~~~~~~~~~~~~~~ - -*Some* bundles require you to import routing configuration. Check for references -to the bundle in ``app/config/routing.yml`` and ``app/config/routing_dev.yml``. -If you find any references, remove them completely. - -2.2 Remove Bundle Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some bundles contain configuration in one of the ``app/config/config*.yml`` -files. Be sure to remove the related configuration from these files. You can -quickly spot bundle configuration by looking for an ``acme_demo`` (or whatever -the name of the bundle is, e.g. ``fos_user`` for the FOSUserBundle) string in -the configuration files. - -3. Remove the Bundle from the Filesystem ----------------------------------------- - -Now you have removed every reference to the bundle in your application, you -should remove the bundle from the filesystem. The bundle will be located in -`src/` for example the ``src/Acme/DemoBundle`` directory. You should remove this -directory, and any parent directories that are now empty (e.g. ``src/Acme/``). - -.. tip:: - - If you don't know the location of a bundle, you can use the - :method:`Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface::getPath` method - to get the path of the bundle:: - - dump($this->container->get('kernel')->getBundle('AcmeDemoBundle')->getPath()); - die(); - -3.1 Remove Bundle Assets -~~~~~~~~~~~~~~~~~~~~~~~~ - -Remove the assets of the bundle in the web/ directory (e.g. -``web/bundles/acmedemo`` for the AcmeDemoBundle). - -4. Remove Integration in other Bundles --------------------------------------- - -Some bundles rely on other bundles, if you remove one of the two, the other -will probably not work. Be sure that no other bundles, third party or self-made, -rely on the bundle you are about to remove. - -.. tip:: - - If one bundle relies on another, in most cases it means that it uses - some services from the bundle. Searching for the bundle alias string may - help you spot them (e.g. ``acme_demo`` for bundles depending on AcmeDemoBundle). - -.. tip:: - - If a third party bundle relies on another bundle, you can find that bundle - mentioned in the ``composer.json`` file included in the bundle directory. diff --git a/changelog.rst b/changelog.rst deleted file mode 100644 index 4936ec7f971..00000000000 --- a/changelog.rst +++ /dev/null @@ -1,2712 +0,0 @@ -.. index:: - single: CHANGELOG - -.. !! CAUTION !! - This file is automatically generated. Do not add new changelog - items when preparing a pull request. - -The Documentation Changelog -=========================== - -This documentation is always changing: All new features need new documentation -and bugs/typos get fixed. This article holds all important changes of the -documentation. - -.. tip:: - - Do you also want to participate in the Symfony Documentation? Take a look - at the ":doc:`/contributing/documentation/overview`" article. - -October, 2016 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6958 `_ [FrameworkBundle] add support for prioritizing form type extension tags (dmaicher) -* `#7074 `_ [PhpUnitBridge] Doc for @expectedDeprecation & new configuration env vars (nicolas-grekas) -* `#6947 `_ [Cache] Add chapter about invalidation, tags, etc. (nicolas-grekas) -* `#7021 `_ [ServiceContainer] Remove implementation details of private services (lemoinem) -* `#7023 `_ Added useful debug commands in the debug documentation (hiddewie) -* `#6946 `_ [VarDumper] Adding semantics with metadata (nicolas-grekas) -* `#6949 `_ Update ProgressBar docs with regress information (jameshalsall) -* `#7019 `_ [Framework] Update link-to-source mapping definition (nicolas-grekas) -* `#6938 `_ Document new utf8 option of Routing component (mickaelandrieu) -* `#6932 `_ Explain the limitations of the custom messages in UniqueEntity (javiereguiluz) -* `#6876 `_ Updated single command How to (mickaelandrieu) -* `#6870 `_ [Debug] Added configuration reference for new debug options (lyrixx) -* `#6783 `_ Routing: add explanation for the "_fragment" parameter (alexislefebvre) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#7049 `_ Fix the platform.sh builds (wouterj) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#7090 `_ Remove suggestion to change the `.class` parameters (mpdude) -* `#7095 `_ Also mention "hasXXX" methods as validation targets (mpdude) -* `#7091 `_ A bracket was missed (hpatoio) -* `#7097 `_ link to specific HTTP Cache RFC (snoek09) -* `#7098 `_ Improved Redirecting paragraph of Testing page (ShinDarth) -* `#7100 `_ Update birthday.rst (angyvolin) -* `#7020 `_ Added jQuery symfony-collection plugin to Form collection docs (hiddewie) -* `#7086 `_ Update bug documentation input console /console/input.rst (Quiss) -* `#7085 `_ Update custom_authentication_provider.rst (BatsaxIV) -* `#7083 `_ update github example (hootlex) -* `#7082 `_ Update metadata.rst (fberthereau) -* `#7078 `_ Simple typo fix (Renrhaf) -* `#7077 `_ remove caution message (snoek09) -* `#7073 `_ Accept only array in TagAwareAdapter::invalidateTags() (Koc) -* `#7048 `_ [#6938] tweak the wording a bit (xabbuh) -* `#7061 `_ Change link to docs instead repo (mik-laj) -* `#7066 `_ Remove erroneous placeholder text (regularjack) -* `#7068 `_ Remove double spaces in some YAML configuration (michaelperrin) -* `#7069 `_ use FCQN to reference the form type in add() (xabbuh) -* `#6785 `_ Twig reference: Add links from routing functions to special routing parameters (alexislefebvre) -* `#7043 `_ [Serializer] Move the see also block in the Learn More section (dunglas) -* `#7035 `_ Redirect /form to /forms for consistency (wouterj) -* `#7054 `_ Fix IS_AUTHENTICATED_FULLY annotation (mschobner) -* `#7044 `_ Add Nginx configuration to environment variables (peterkokot) -* `#6928 `_ Update the Apache Router article to deprecate it entirely (javiereguiluz) -* `#7053 `_ Minor improvements for the contribution guide (javiereguiluz) -* `#7050 `_ use single quotes for YAML strings (snoek09) -* `#7047 `_ Fix typo in doctrine.rst (to manage) (lacyrhoades) -* `#7046 `_ Fix incorrect callback validation example (mvar) -* `#7034 `_ Changed RFC links from drafts to proposed standarts (a-ast) -* `#7038 `_ Remove a dead link to the old PR header (dunglas) -* `#7037 `_ Fix a typo in the serializer doc (dunglas) -* `#7036 `_ Fix 404 error link for American English Oxford Dictionary (peterkokot) -* `#6980 `_ Use strict comparison (greg0ire) -* `#7025 `_ Update buisness-logic (zairigimad) -* `#7027 `_ Remove dash prefix from route name (bocharsky-bw) -* `#7028 `_ A few minor tweaks (bocharsky-bw) -* `#7029 `_ refer to Symfony instead of Symfony2 (snoek09) -* `#7031 `_ Capitalize the time designator (simoheinonen) -* `#7018 `_ Reorder arguments: $request as the first argument (bocharsky-bw) -* `#7014 `_ Add a note about Filesystem:mkdir behavior (mickaelandrieu) -* `#6886 `_ Update controllers.rst (asandjivy) - -September, 2016 ---------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6989 `_ Added paths options in Framework::translator configuration (mickaelandrieu) -* `#6926 `_ [Serializer] Add information about name converter parameter (michaelperrin) -* `#6891 `_ [VarDumper] Doc for the Data::seek() method (nicolas-grekas) -* `#6890 `_ [Routing] Add doc about unicode requirements (nicolas-grekas) -* `#6944 `_ Update doc about IDE file link format (nicolas-grekas) -* `#6968 `_ [Console] Add multiple options for the output example (SpacePossum) -* `#6956 `_ [Filesystem] Add documentation for the readlink method (tgalopin) -* `#6973 `_ Callback doesn't have to be static (patrick-mcdougle) -* `#6976 `_ [Finishing][Serializer] Document the encoders (Ener-Getick, weaverryan) -* `#6622 `_ Documentation for YAML flags added in 3.1 (dantleech) -* `#6962 `_ [Bridge/PhpUnit] doc bin/simple-phpunit (nicolas-grekas, weaverryan) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#7016 `_ Add empty parentheses to method names (bocharsky-bw) -* `#7015 `_ Replace title placeholder name with slug (bocharsky-bw) -* `#7009 `_ Update form_dependencies.rst: fix DI (ReDnAxE) -* `#7010 `_ Update service_decoration.rst (lamari) -* `#7008 `_ Missing semicolon (Saphyel) -* `#6979 `_ Add specific tip about the http-kernel component (greg0ire) -* `#6686 `_ Update installation.rst (mgkimsal) -* `#7007 `_ normalize versionadded wording (xabbuh) -* `#6990 `_ replace dirname() call with .. (xabbuh) -* `#6992 `_ [Serializer] versionadded directive for name_converter option (xabbuh) -* `#7005 `_ Use new array syntax and make a few minor tweaks (bocharsky-bw) -* `#7005 `_ Use new array syntax and make a few minor tweaks (bocharsky-bw) -* `#7004 `_ Tweak URL - CMF project moved to the other repo (bocharsky-bw) -* `#7001 `_ Several typo fixes (emirb) -* `#7000 `_ Several typo fixes (emirb) -* `#6999 `_ Several typo fixes (emirb) -* `#6997 `_ Update console.rst (adyassine) -* `#6917 `_ [Finder] document array use for locations (mickaelandrieu) -* `#6993 `_ Update create_custom_field_type.rst (yceruto) -* `#6993 `_ Update create_custom_field_type.rst (yceruto) -* `#6994 `_ [Reference] remove 2.8 versionadded directive (xabbuh) -* `#6984 `_ Update deployment.rst (adyassine) -* `#6995 `_ the least -> least (konrados) -* `#6996 `_ fix reference syntax (xabbuh) -* `#6991 `_ Update heroku.rst (adyassine) -* `#6934 `_ Update events.rst (asandjivy) -* `#6897 `_ Update voters.rst (asandjivy) -* `#6920 `_ [Config] Note about bundle priority for PrependExtensionInterface (wodor) -* `#6905 `_ Change example of ignoring dependencies for yaml (Integrity-178B) -* `#6885 `_ [FormComponent]Fix wrong mention in side note (rendler-denis) -* `#6911 `_ Article about logout. (BorodinDemid) -* `#6923 `_ Clarify by_reference use (jxmallett) -* `#6942 `_ Updated the "Build a Login Form" article (javiereguiluz) -* `#6931 `_ [Guard] Improve clarity using the configured provider (chalasr) -* `#6933 `_ Misplaced paragraph about placeholders in routing.rst (antoin-m) -* `#6930 `_ Use Terminal lexer for console examples (wouterj) -* `#6893 `_ Update entity_provider.rst (asandjivy) -* `#6895 `_ fixing $formatLevelMap array values (zrashwani) -* `#6935 `_ Use the standard cache and logs dir for the micro kernel example (javiereguiluz) -* `#6970 `_ Fix subject/verb agreement (micheal) -* `#6971 `_ Update composer.rst (TravisCarden) -* `#6983 `_ Update voters.rst (seferov) -* `#6986 `_ Fixed directory name typo (JoeThielen) -* `#6988 `_ fix link role syntax (xabbuh) -* `#6960 `_ [Reference] add back the option's description (xabbuh) -* `#6987 `_ Update input.rst (adyassine) -* `#6974 `_ Fix minor typo in security chapter How to Build a Traditional Login Form (peterkokot) -* `#6941 `_ Mentioned the "Symfony Upgrade Fixer" in the upgrade article (javiereguiluz) -* `#6936 `_ Improved the title of Validation Groups article to make it easier to find (javiereguiluz) -* `#6925 `_ Method "$this->getMock()" is deprecated (JohnnyEvo) -* `#6922 `_ [Config] Add ExprBuilder::ifEmpty() (ogizanagi) -* `#6964 `_ Fix typo in validator example (svenluijten) -* `#6945 `_ Fixed indentation issues in alias_private article (javiereguiluz) -* `#6954 `_ Typo fix in tags.rst (NoScopie) -* `#6955 `_ Typo in the class name. (pythagor) - -August, 2016 ------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6903 `_ Remove reference to profiler lifetime property that was removed in 3.x (jameshalsall) -* `#6908 `_ Add deprecation warnings to relevant profiler options (jameshalsall) -* `#5974 `_ [PropertyInfo] Add Component Documentation (zanderbaldwin) -* `#6765 `_ [Contributing] [Standards] Do not use spaces inside/around offset accessors (phansys) -* `#6746 `_ Removing the alias stuff - not required after symfony/symfony#17074 (weaverryan) -* `#6798 `_ Finishing Validator Docs (wouterj, mickaelandrieu, javiereguiluz, weaverryan) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6915 `_ Fix the error in code example (kruglikov) -* `#6907 `_ fix wrong variable name in OptionsResolver example (dincho) -* `#6904 `_ Update create_custom_field_type.rst (tuanalumi) -* `#6892 `_ Update custom_provider.rst (asandjivy) -* `#6884 `_ service_container : fix php Definition instance (ReDnAxE) -* `#6883 `_ [Routing] Fix a route path in a routing example (thomasbisignani) -* `#6869 `_ Update templating.rst (asandjivy) -* `#6822 `_ Adjust Application use statement (kvdnberg) -* `#6881 `_ Error in CSRF example code snippet (makoru-hikage) -* `#6863 `_ Update argument_value_resolver.rst (asandjivy) -* `#6852 `_ Fix Cache Pools: SQLite3Cache constructor argument (wimme002) -* `#6848 `_ Fix Varnish 4 code example (Dreimus) -* `#6845 `_ Fixed the extension of a logging article (javiereguiluz) -* `#6790 `_ Simplex\Framework contructor - remove extra parameter (alchimik) -* `#6793 `_ Simplex\Framework - add argumentResolver property (alchimik) -* `#6800 `_ Fix missing function name (JosefVitu) -* `#6843 `_ fix index directive syntax (xabbuh) -* `#6784 `_ Fix CS for form templates locations (javiereguiluz) -* `#6797 `_ fix FlattenException namespace (alchimik) -* `#6787 `_ Fix reference to output object (micheal) -* `#6761 `_ fixed missing level in namespace (themasch) -* `#6757 `_ Fix typo in external_parameters.rst (gmorel) -* `#6754 `_ Add missing use statements to data collector example (richardmiller) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6921 `_ Fix var_dumper advanced usage link (ogizanagi) -* `#6913 `_ [Controller Description] Fix typos and class link (rendler-denis) -* `#6909 `_ DumpFile() third argument is deprecated and doesn't exists anymore (mickaelandrieu) -* `#6901 `_ [EventDispatcher] paragraph duplicated (ReDnAxE) -* `#6899 `_ Update access_control.rst (asandjivy) -* `#6898 `_ Fixes after tonight's merge round (wouterj) -* `#6896 `_ Tweaks to property info component (weaverryan) -* `#6888 `_ Update http_kernel_controller_resolver.rst (audiua) -* `#6887 `_ [FormComponent] Fix typos (rendler-denis) -* `#6849 `_ Link to inversedBy/mappedBy documentation (soulchainer, wouterj) -* `#6846 `_ Adds bin folder creation instruction (joelrfcosta) -* `#6835 `_ Updated the instructions to build docs locally (javiereguiluz) -* 5a5720fe minor #6834 Refactored how to get the container in a test (javiereguiluz) -* `#6814 `_ Created the main article about "deployment" (javiereguiluz) -* `#6882 `_ Update serializer.rst (seferov) -* `#6880 `_ Remove extra quotes from ExprBuilder::thenInvalid() usage (chalasr) -* `#6878 `_ missing "`" (jevgenijusr) -* `#6877 `_ added lyrixx to the core team (fabpot) -* `#6867 `_ Add a class specificity for SplFileInfo text (rendler-denis) -* `#6872 `_ Remove redundant verb (julienfalque) -* `#6866 `_ Deprecated message with "true" parameter (wazz42) -* `#6862 `_ Remove unused JsonResponse dependency in example (Farskies) -* `#6855 `_ [Form] Use composer require instead of modifying composer.json (wouterj) -* `#6853 `_ Logrotate moved to GitHub (wouterj) -* `#6851 `_ Update lazy_services.rst (takeit) -* `#6794 `_ Added a new section to the page templating/global_vars using a EVListener (piet, Piet Bijl) -* `#6850 `_ Remove outdated reference (hvt) -* `#6824 `_ Service naming convension (orions) -* `#6825 `_ update `cache` and `logs` folder locations (georgiana-gligor) -* `#6829 `_ Fix a typo in an HTTP Cache code example (aybbou) -* `#6833 `_ Fixed a syntax issue in custom_constraint article (javiereguiluz) -* `#6842 `_ Fixed service name (jeremyFreeAgent) -* `#6803 `_ Remove complex speech pattern (micheal) -* `#6805 `_ Remove colloquialism "hold on" (micheal) -* `#6796 `_ Remove AcmeDemoBundle references (michaelcullum) -* `#6786 `_ Subject-verb agreement (micheal) -* `#6759 `_ Fix tense and sentence length (aalaap) -* `#6748 `_ Fix web/front.php (ranqiangjun) -* `#6820 `_ Fixed the main index page redirections (javiereguiluz) -* `#6819 `_ Fixed the redirection for "upgrade" articles (javiereguiluz) -* `#6812 `_ Fixed a Console article redirection (javiereguiluz) -* `#6807 `_ Fixed the redirection of the cookbook/psr7 article (javiereguiluz) -* `#6806 `_ Fixed the redirection of some cache articles (javiereguiluz) -* `#6808 `_ Fixed a DI redirection (javiereguiluz) -* `#6810 `_ Fixed the redirection of the previous "performance" book chapter (javiereguiluz) -* `#6816 `_ Added all the missing "index pages" redirections (javiereguiluz) - -July, 2016 ----------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6744 `_ [Form] Ambiguous block prefixes render incorrectly (foaly-nr1) -* `#6611 `_ Discourage the use of controllers as services (javiereguiluz) -* `#5672 `_ Add constants to BC promise (WouterJ) -* `#6737 `_ Document the file() controller helper (javiereguiluz) -* `#6707 `_ Describe serialization config location in cookbook (jcrombez, WouterJ) -* `#6726 `_ Use getParameter method in controllers (peterkokot) -* `#6727 `_ Updated the condition to display console name (mickaelandrieu) -* `#6701 `_ [CS] Avoid using useless expressions (phansys) -* `#6422 `_ Documented the ArgumentResolver along the ControllerResolver (iltar) -* `#6705 `_ [Process] Introduce InputStream and iterator for output (nicolas-grekas) -* `#6673 `_ Caution about impersonation not compatible with pre authenticated (pasdeloup) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6634 `_ Update custom_constraint.rst (axelvnk) -* `#6719 `_ [Components][Browser-Kit]Fix typo with CookieJar (Denis-Florin Rendler) -* `#6717 `_ Cache Component: Fix SQLite3Cache instanciation (ReDnAxE) -* `#6687 `_ Namespace fix (JhonnyL) -* `#6714 `_ UppercaseRot13Transformer wrong class name used (jevgenijusr) -* `#6704 `_ Encountered an error when following the steps for contribution (chancegarcia) -* `#6708 `_ Routes should be just imported, not mounted (OndraM) -* `#6708 `_ Routes should be just imported, not mounted (OndraM) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6778 `_ fix syntax errors (xabbuh) -* `#6777 `_ fix code block indentation (xabbuh) -* `#109 `_ after merge fixes (xabbuh) -* `#107 `_ fix bugs due to choosing the wrong base branch (xabbuh) -* `#108 `_ fix another bug due to choosing the wrong branch (xabbuh) -* `#105 `_ fix bugs due to choosing the wrong base branch (xabbuh) -* `#102 `_ Updated the Global Composer Installation article (javiereguiluz) -* `#101 `_ Update some screenshots to wrap them with a browser window (javiereguiluz) -* `#104 `_ complete component cross references (xabbuh) -* `#106 `_ some minor tweaks (xabbuh) -* `#98 `_ Remove mentions of cookbook/book (WouterJ) -* `#97 `_ Rewrote the Console articles (WouterJ, javiereguiluz) -* `#99 `_ Rename cache/ to http_cache/ (WouterJ) -* `#100 `_ Add file extension to SOAP article (WouterJ) -* `#92 `_ Make usage of "The" in the edition list consistent (WouterJ) -* `#91 `_ Create a section for "Getting Started" so we can generate a book (javiereguiluz) -* `#86 `_ bootstrapping Workflow component docs (weaverryan) -* `#77 `_ Proofing the controller chapter (weaverryan) -* `#90 `_ Fixed doc build issues (javiereguiluz) -* `#82 `_ Bootstrapping property info doc (weaverryan) -* `#79 `_ Shortening the setup section (weaverryan) -* `#81 `_ Merging setup and install directories (weaverryan) -* `#84 `_ Bootstrapping the validator components (weaverryan) -* `#87 `_ Moving the email guide to the top level (weaverryan) -* `#88 `_ Moving event_dispatcher/event_listener.rst -> event_dispatcher.rst (weaverryan) -* `#78 `_ Move redirection_map from root (WouterJ) -* `#54 `_ split the Security chapter (xabbuh) -* `#53 `_ split the Validation chapter (xabbuh) -* `#63 `_ Readded removed versionadded directives (WouterJ) -* `#55 `_ Created the "Set Up" topic (WouterJ) -* `#62 `_ Rename includes directory to _includes (WouterJ) -* `#61 `_ Fix install/upgrade references (WouterJ) -* `#58 `_ The no-brainer topic merges/removal (WouterJ) -* `#56 `_ Fix build errors (WouterJ) -* `#39 `_ Deleting index files - using globbing (weaverryan, WouterJ) -* `#47 `_ Move nested service container articles to sub-topic root (WouterJ) -* `#50 `_ Move images to _images and group by topic (WouterJ) -* `#32 `_ Move all cookbook contents (javiereguiluz) -* `#28 `_ split the routing chapter (xabbuh) -* `#30 `_ Moved the rest of the book chapters (javiereguiluz) -* `#24 `_ Moved book chapters out of the book (javiereguiluz) -* `#20 `_ Creating the Controller topic (xabbuh) -* `#6747 `_ Correcting reference to ``isSuccessful()`` method for Process (aedmonds) -* `#6600 `_ Removing some extra details from #6444 (weaverryan) -* `#6715 `_ [Book] Remove DI extension info and link the cookbook article instead (WouterJ) -* `#6745 `_ Branch fix (Talita Kocjan Zager, weaverryan) -* `#6743 `_ Finishing #6252 (Talita Kocjan Zager, weaverryan) -* `#6656 `_ Clarify usage of handler channel configuration (shkkmo) -* `#6664 `_ replace occurrences of `_ Add little comment indicating meaning of $firewall variable (ruslan-fidesio, WouterJ) -* `#6734 `_ Add little caution to add service id for @Route annotation (DHager, WouterJ) -* `#6735 `_ Change _method parameter versionadded note (sfdumi, WouterJ) -* `#6736 `_ Use message argument for PHPunit assert() functions (SimonHeimberg, WouterJ) -* `#6739 `_ fix list item termination character (xabbuh) -* `#6218 `_ path() explanation inside templating + Minor formatting changes (sfdumi) -* `#6559 `_ Update lazy_services.rst (hboomsma) -* `#6733 `_ [DX] Form Types location contradicts Best Practices (pbowyer) -* `#6264 `_ Update email.rst (mikaelz) -* `#6633 `_ Added escaping tip (xDaizu) -* `#5464 `_ Removed the glossary (WouterJ) -* `#6665 `_ use PDO prepared statement - avoid straw man (dr-matt-smith) -* `#6700 `_ Update monolog.rst (zhil) -* `#6720 `_ [Component][ClassLoader]Remove invalid note (rendler-denis) -* `#6613 `_ Clarify documentation on serving files (raphaelm) -* `#6723 `_ Require latest Symfony version (yhoiseth) -* `#6721 `_ [Finder] Fixed typo in RealPath method on SplFileInfo class (acrobat) -* `#6716 `_ Typo fix "they the name" => "that the name" (jevgenijusr) -* `#6702 `_ Removed empty ``Notes on previous versions`` (mickaelandrieu) -* `#6709 `_ Fix URL in http basic screenshot (WouterJ) -* `#6706 `_ Update "How to Authenticate Users with API Keys" (gondo, WouterJ) -* `#5892 `_ Updated the session proxy article (javiereguiluz) -* `#6699 `_ [Cache] add versionadded directive (xabbuh) -* `#6697 `_ [Asset] add versionadded directive (xabbuh) -* `#6698 `_ [Ldap] add versionadded directive (xabbuh) - -June, 2016 ----------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6515 `_ Added the documentation for the Cache component (javiereguiluz) -* `#6623 `_ [Console] Adapt doc for easier testing of commands needing user inputs (chalasr) -* `#6690 `_ Added an example for a different method of verbosity level usage. (smatyas) -* `#6648 `_ Process: callbacks now allowed when output disabled (avindra) -* `#6587 `_ Updating recommended email settings for monolog (jorgelbg) -* `#6617 `_ [WSSE] - Using a PSR6 cache instead of file cache (Nyholm) -* `#6438 `_ Added docs about ArgumentValueResolvers (iltar) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6679 `_ Invalid PHP return statement (JohnnyEvo) -* `#6675 `_ Update broken links to default VCL files (sgrodzicki) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6597 `_ [Validator] Add shorter examples using the default option (alexmart) -* `#6696 `_ Typo fix (jevgenijusr) -* `#6693 `_ Add missing parameter (rodnaph) -* `#6614 `_ Updated the CS rule about return null; and return; (javiereguiluz) -* `#6680 `_ Fix ldap security examples (jvasseur) -* `#6692 `_ Update date.rst - Fixes typo (fdarre) -* `#6689 `_ Hard values for the driver option (iltar) -* `#6685 `_ NullOutput should be passed to $command->run() (Ma27) -* `#6676 `_ Removed 'html' from the component description (naroga) -* `#6674 `_ CheckStyle in Voters cookbook (JakeFr) -* `#6672 `_ [Book][Testing] remove Symfony core testing note (xabbuh) -* `#6670 `_ Fix typo 'even' >> 'event' in event_listener.rst (kuusas) -* `#6667 `_ [Contributing][Code] fix list item terminators (xabbuh) -* `#6616 `_ Better explain the mandatory/convention location of some elements (rcousens, javiereguiluz) -* `#6628 `_ Fix for #6625 (kix) -* `#6668 `_ [Contributing][Code] remove PHPUnit requirement (xabbuh) -* `#6654 `_ Update upload_file.rst (liubinas) -* `#6650 `_ fix dumper default representation (Jamal Youssefi) -* `#6652 `_ ``Finder::path()`` method matching directories and files (soyuka) -* `#6662 `_ preg_match throw an warning (nicolae-stelian) -* `#6651 `_ [#6438] some tweaks to the argument value resolver (xabbuh) -* `#6658 `_ [Process] tweak a sentence (xabbuh) -* `#6599 `_ Fixed null description of query_builder option (HeahDude) -* `#6638 `_ swap terminate and exception event descriptions (xabbuh) -* `#6615 `_ Minor grammar fix (aalaap) -* `#6637 `_ Update security.rst (norbert-n) -* `#6644 `_ [Console] Fix wrong quotes in QuestionHelper::setInputStream() (chalasr) -* `#6645 `_ Fix bootstrap class name help-block (foaly-nr1) -* `#6642 `_ do not reference unused interface (xabbuh) -* `#6641 `_ [Book][Form] fix reference to renamed document (xabbuh) -* `#6579 `_ Added callable validation_groups example (gnat42) -* `#6626 `_ reflect the EOM of Symfony 2.3 (xabbuh) -* `#6631 `_ Fix console.exception and console.terminate order (Julien Falque) -* `#6629 `_ Update options_resolver.rst (atailouloute) -* `#6627 `_ Fixed a typo in cookbook/security/entity_provider (michaeldegroot) -* `#6618 `_ Added a note about coding standards and method arguments (javiereguiluz) - -May, 2016 ---------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6402 `_ [RFR] Documentation for the Ldap component (csarrazi) -* `#6040 `_ Remove old File Upload article + improve the new one (WouterJ) -* `#6412 `_ added a maintenance document (fabpot) -* `#6554 `_ Adding information about using the date type as usable date picker field (weaverryan) -* `#6590 `_ Added note on YAML mappings as objects (dantleech) -* `#6378 `_ refs #5898 Fix updates of testing.rst for 3.0 (guilliamxavier) -* `#6583 `_ Adding a description for the use_microseconds parameter introduced in MonologBundle v2.11 (jorgelbg) -* `#6582 `_ Advanced YAML component usage (dantleech) -* `#6594 `_ Allowed to return null for query_builder (JonEastman) -* `#6502 `_ Added the json() shortcut to the controller chapter (dfridrich, javiereguiluz) -* `#6593 `_ Adding the payload parameter to the callback examples (jorgelbg) -* `#6405 `_ Added the explanation about addClassesToCompile() method (javiereguiluz) -* `#6539 `_ Documented the "autoescape" TwigBundle config option (javiereguiluz) -* `#5574 `_ [2.7] Update Twig docs for asset features (javiereguiluz, WouterJ) -* `#6433 `_ Documented the detectCorrupted and corruptedMessage options (javiereguiluz) -* `#6406 `_ Added a note about the new "disabled" option for PHPUnitBridge (javiereguiluz) -* `#6302 `_ [Form] add ``choice_translation_domain`` option to date types (HeahDude) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6619 `_ Fix wrong variable name in comment (zanardigit) -* `#6608 `_ deprecated 'datettime' Form Type (dsmink) -* `#6606 `_ fix #6602 (yamiko-ninja) -* `#6578 `_ [Cookbook][Profiler] Fix arguments for Profiler::find() (hason) -* `#6546 `_ Make ClockMock Tests\\ namespace matching less specific (teohhanhui) -* `#6564 `_ [PhpUnitBridge] Remove section about clock mocking (z38) -* `#6552 `_ Typo fix in the Serializer deserialization example for existing object (fre5h) -* `#6545 `_ Replace property_accessor by property_access (jbenoit2011) -* `#6561 `_ About Templating Naming Pattern (raulconti) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6601 `_ Deprecated usage of AsseticBundle removed from bundles.rst (Vehsamrak) -* `#6624 `_ remove versionadded directive for 2.x features (xabbuh) -* `#6620 `_ Move PSR to correct place on index page (WouterJ) -* `#6609 `_ Use a better escaping mechanism for data-prototype attr (javiereguiluz) -* `#6380 `_ [book] [validation] Constraints can be applied to an entire class too (sustmi) -* `#6459 `_ Remove choices as values in 3.0 (weaverryan) -* `#6444 `_ [Form] fixed EntityType choice options (HeahDude) -* `#6367 `_ Simplified the contribution article for Symfony Docs (javiereguiluz) -* `#6419 `_ Update routing.rst (tamtamchik) -* `#6598 `_ [Yaml] use static Yaml API (xabbuh) -* `#6589 `_ Clarify signed requests in the ESI renderer (WouterJ) -* `#6596 `_ Fixed query_builder option (HeahDude) -* `#6595 `_ Added a note about "encoding vs. hashing" passwords (javiereguiluz) -* `#6581 `_ [#6431] changing "Simple Example" to use implode/explode (mccullagh) -* `#6585 `_ 6555 link to Download instructions page & Windows executable (snoek09) -* `#6588 `_ Update configuration.rst (superhaggis) -* `#6591 `_ 5953 use kernel events constants (snoek09) -* `#6592 `_ [Form] Making the name property private to be more realistic (weaverryan) -* `#6541 `_ Trusted proxies were removed when URL signing took over (rawkode) -* `#6586 `_ 6338 use csrfManager instead of csrfProvider (snoek09) -* `#6401 `_ Added Link to Cmder (c33s) -* `#6391 `_ Fix mem leak in example doctrine testing (nicolas-grekas) -* `#6087 `_ Add a note about needing to install proxy-manager (mcfedr) -* `#6569 `_ [Reference] change namespace to point to new class (xabbuh) -* `#6553 `_ EntityType: query_builder link to usage (weaverryan) -* `#6572 `_ Edited BowerPHP tip (SecondeJK) -* `#6575 `_ Rename command logging services (sroze) -* `#6577 `_ [Cookbook][Profiler] Remove mention of import/export (hason) -* `#6571 `_ [Cookbook][Console] Minor: Fix typo (andreia) -* `#6570 `_ Fix typo (jdreesen) -* `#6568 `_ fix RequestDataCollector class namespace (xabbuh) -* `#6566 `_ Update options_resolver.rst (snake77se) -* `#6548 `_ merge choice_translation_domain files (xabbuh) -* `#6547 `_ remove 2.x versionadded directives (xabbuh) -* `#6563 `_ [PhpUnitBridge] Add versionadded directive to clock mocking section (z38) -* `#6549 `_ drop AppBundle examples in components section (xabbuh) -* `#6562 `_ Remove extra spaces in Nginx template (bocharsky-bw) -* `#6557 `_ [ClassLoader] Add missed link to the external PSR-4 specification (nicolas-grekas, fre5h) -* `#6511 `_ [DependencyInjection] Improved "optional argument" documentation (dantleech) -* `#6455 `_ Editing the Doctrine section to improve accuracy and readability (natechicago) -* `#6526 `_ Documented how to configure Symfony correctly with regards to the Forwarded header (magnusnordlander) -* `#6535 `_ Improved the description of the Twig global variables (javiereguiluz) -* `#6536 `_ [DomCrawler] Removed references to CssSelector (aerialls) -* `#6529 `_ [DependencyInjection] Unquote services FQCN in autowiring examples (chalasr) -* `#6530 `_ [DependencyInjection] Unquote services FQCN in parent-services examples (chalasr) -* `#6517 `_ Add a warning about using same user for cli and web server (pasdeloup) -* `#6504 `_ Improved the docs for the DependencyInjection component (javiereguiluz) -* `#6506 `_ Added a tip about routes and container parameters (javiereguiluz) -* `#6518 `_ Add details about chmod +a vs setfacl (pasdeloup) -* `#6525 `_ [Contributing] use more precise version checker URL (xabbuh) -* `#6528 `_ Fixed a minor indentation issue (javiereguiluz) - -April, 2016 ------------ - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6470 `_ Documented the config options of TwigBundle (javiereguiluz) -* `#6427 `_ [Testing] Explain how to add or remove data in a collection of forms (alexislefebvre) -* `#6465 `_ Added ldap to the list of user providers (AAstakhov) -* `#6437 `_ Improved the documentation about the explicit column widths (javiereguiluz) -* `#6450 `_ [Form] added prototype_data option in CollectionType (kgilden, HeahDude) -* `#6394 `_ Updated Heroku instructions (magnusnordlander) -* `#6388 `_ Added the docs for the @dns-sensitive mocks (javiereguiluz) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6503 `_ Fix typo in from flat PHP to Symfony (gregfriedrice, WouterJ) -* `#6483 `_ Fix typo: signifcantly => significantly (ifdattic) -* `#6482 `_ fixed wrong secret string in array examples (OskarStark) -* `#6460 `_ Update authorization.rst (mantulo) -* `#6451 `_ fix status code in snippet (Barno) -* `#6448 `_ [Form] fixed CollectionType needless option (HeahDude) -* `#6439 `_ Fix form/validation directory path (nemo-) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6522 `_ On line 360 the ``404 Not Found`` header not working (mbrig-co) -* `#6521 `_ The ``$link`` argument must be passed as a reference (mbrig-co) -* `#6523 `_ Fix snippet (ismailbaskin) -* `#6472 `_ Avoid confusion (gerryvdm) -* `#6300 `_ Document constraint validator alias optional (Triiistan) -* `#6513 `_ remove documentation of not supported "verbose" option value (TobiasXy) -* `#6507 `_ fix typo (tyx) -* `#6509 `_ Update http_kernel_httpkernel_class.rst (AchillesKal) -* `#6510 `_ use port 587 in Amazon SES example (snoek09) -* `#6464 `_ Added possible values for access_decision_manager.strategy (AAstakhov) -* `#6469 `_ Tweaks for the DNS-sensitive tests doc (javiereguiluz) -* `#6478 `_ Replace reference to the request service (gerryvdm) -* `#6479 `_ Update php.rst (carlos-granados) -* `#6481 `_ Remove reference to Symfony2 in request-flow.png (Daniel Cotton) -* `#6471 `_ fix broken merge (xabbuh) -* `#6449 `_ [Form] fixed ChoiceType example in CollectionType (HeahDude) -* `#6445 `_ updated the core team (fabpot) -* `#6423 `_ Added a caution note about REMOTE_USER and user impersonation (javiereguiluz) -* `#6452 `_ Use different placeholders in mailer config (sblaut) -* `#6457 `_ Fixed an array notation in comment (serializer.rst) (iltar) -* `#6456 `_ Fixed array [] notation and trailing spaces (iltar) -* `#6420 `_ Added tip for optional second parameter for form submissions. (Michael Phillips) -* `#6418 `_ fix spelling of the flashBag() method (xabbuh) -* `#6432 `_ fixed yaml config error (RickieL) - -March, 2016 ------------ - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6274 `_ Update Doctrine UTF8 docs (mcfedr) -* `#6314 `_ New normalizers (mcfedr) -* `#4971 `_ [DomCrawler] Document images crawler (valeriangalliat) -* `#6296 `_ [Console] Add columns width setter documentation (akeeman) -* `#6282 `_ [Form] fix ``choice_label`` values (HeahDude) -* `#5894 `_ [WIP] Added an article to explain how to upgrade third-party bundles to Symfony 3 (javiereguiluz) -* `#6273 `_ [PHPUnit bridge] Add documentation for the component (theofidry) -* `#6316 `_ [Validator] Added docs about the format option (dosten) -* `#6291 `_ fortrabbit deployment guide + index listing (ostark) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6377 `_ Update "bootstrap.php.cache" to "autoload.php" (guilliamxavier) -* `#6368 `_ [cookbook] Made list of form types more consistent (AAstakhov) -* `#6366 `_ Removed server:stop code block for 2.3 (theyoux) -* `#6347 `_ Add a note about enabling DebugBundle to use VarDumper inside Symfony (martijn80, javiereguiluz) -* `#6320 `_ Fixed typo in path (timhovius) -* `#6334 `_ Fixed yaml configuration of app.exception_controller (AAstakhov) -* `#6322 `_ [DependencyInjection] fix autowiring docs (eXtreme) -* `#6315 `_ Remove third parameter from createFormBuilder call (Hocdoc) -* `#6324 `_ Fixed UserCheckerInterface importing (VIs-a-vis) -* `#6326 `_ Missing svn:ignore (f-plante) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6404 `_ fixed a typo (RickieL) -* `#6409 `_ Update 'date' to DateType::class in form.rst (iltar) -* `#6411 `_ Fixed a typo in configuration-block (VarunAgw) -* `#6414 `_ stick to Sphinx 1.3.x for the moment (xabbuh) -* `#6399 `_ Fixed wrong code examples for Isbn constraint (AAstakhov) -* `#6397 `_ Fix typo in SwitchUserListener file name (luxifer) -* `#6390 `_ Reworded the example about $deep param (Oliboy50, javiereguiluz) -* `#6381 `_ [Form] [Cookbook] Correctly setup unit tests with dependencies (corphi) -* `#6382 `_ unused use instructions (bshevchenko) -* `#6365 `_ Removed the PR table example (this is now included by GitHub template) (javiereguiluz) -* `#6363 `_ Removed info about reducing visibility for private (AAstakhov) -* `#6362 `_ [book] Updated link to Translatable Extension (AAstakhov) -* `#6336 `_ Added minor clarification (ThomasLandauer) -* `#6303 `_ Tweaked the Symfony Releases page (javiereguiluz) -* `#6360 `_ Editing the Doctrine section to improve accuracy and readability (natechicago) -* `#6352 `_ [book] controller ch review, part 3 (Talita Kocjan Zager) -* `#6351 `_ [book] controller ch review, part 2 (Talita Kocjan Zager) -* `#6349 `_ [book] controller ch review, part 1 (Talita Kocjan Zager) -* `#6369 `_ Minor corrections (sfdumi) -* `#6370 `_ Fixed typo (tabbi89) -* `#6371 `_ Fix escaping of backtick inside double back-quotes (guilliamxavier) -* `#6364 `_ [reference] [constraints] added missing colon character for Image constraint documentation in YAML format. (hhamon) -* `#6345 `_ Remove link-local IPv6 address (snoek09) -* `#6219 `_ Point that route parameters are also Request attributes (sfdumi) -* `#6348 `_ [best practices] mostly typos (Talita Kocjan Zager) -* `#6350 `_ Fix reference to app folder (kainjow) -* `#6275 `_ [quick tour] mostly typos (Talita Kocjan Zager) -* `#6305 `_ Mention IvoryCKEditorBundle in the Symfony Forms doc (javiereguiluz) -* `#6331 `_ Rename DunglasApiBundle to ApiPlatform (sroze) -* `#6328 `_ Update extension.rst - added caution box for people trying to remove the default file with services definitions (Pavel Jurecka) -* `#6343 `_ Replace XLIFF number ids by strings (Triiistan) -* `#6344 `_ Altered single / multiple inheritance sentence (outspaced) -* `#6330 `_ [Form] reorder EntityType options (HeahDude) -* `#6337 `_ Fix configuration.rst typo (gong023) -* `#6295 `_ Update tools.rst (andrewtch) -* `#6323 `_ [DependencyInjection] Add Autowiring keyword (theofidry) -* `#6325 `_ Minor error (ThomasLandauer) -* `#6311 `_ Improved TwigExtension to show default values and optional arguments (javiereguiluz) -* `#6286 `_ [HttpFoundation] Fix typo for ParameterBag getters - 3.0 (rendler-denis) -* `#6267 `_ [Form] fix 'data_class' option in EntityType (HeahDude) -* `#6281 `_ Change isValid to isSubmitted. (mustafaaloko) - -February, 2016 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6172 `_ move assets options from templating to assets section and add base_path documentation (snoek09) -* `#6021 `_ mention routing from the database (dbu) -* `#6032 `_ [DependencyInjection] Autowiring doc (dunglas) -* `#6233 `_ Document translation_domain for choice fields (merorafael, WouterJ) -* `#5655 `_ Added doc about Homestead's Symfony integration (WouterJ) -* `#5886 `_ [2.8] Add "How to Use Multiple Guard Authenticators" cookbook documentation (mheki) -* `#6072 `_ Add browserkit component documentation (yamiko, yamiko-ninja, robert Parker, javiereguiluz) -* `#6243 `_ Add missing getBoolean() method (bocharsky-bw) -* `#6076 `_ added a sentence about the HttpException::setHeaders method (smatyas) -* `#6231 `_ Use hash_equals instead of StringUtils::equals (WouterJ) -* `#6186 `_ [Console] Add FormatterHelper::truncate docs (mheki) -* `#5530 `_ [Cookbook, Security] Added user_checkers.rst (iltar) -* `#5920 `_ Document automatic registration of extension compiler passes (WouterJ) -* `#5724 `_ Describe configuration behaviour with multiple mailers (xelan) -* `#6077 `_ fixes #5971 (vincentaubert) -* `#5483 `_ [FrameworkBundle] Name converter of Serializer (dunglas) -* `#6156 `_ [reference] [form] [options] fix #6153 (HeahDude) -* `#6104 `_ Fix #6103 (zsturgess) -* `#6058 `_ Update Testing Form Types article for 2.8 refactorings (WouterJ) -* `#5856 `_ Reworded the "How to use Gmail" cookbook article (javiereguiluz) -* `#6230 `_ Add annotation to glossary (rebased) (DerStoffel) -* `#5642 `_ Documented label_format option (WouterJ) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6292 `_ Fix setting permission for var subdirectories (voda) -* `#5995 `_ Update dev_environment.rst (gonzalovilaseca) -* `#6240 `_ [#6224] some tweaks (xabbuh) -* `#5513 `_ [load_balancer_reverse_proxy ] Always use 127.0.0.1 as a trusted proxy (ruudk) -* `#6081 `_ [cookbook New project] Fix symfony version and initial add (bigs21) -* `#6124 `_ [cookbook] Add annotations block and fix regex (peterkokot) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6308 `_ fix literal syntax (xabbuh) -* `#6299 `_ Removed True and False constraints from reference (edefimov) -* `#6298 `_ Update dependency_injection.rst because it has an error. (joserprieto) -* `#6263 `_ [Cookbook][Debugging] reflect behavior changes in cache generation (xabbuh) -* `#6251 `_ To use annotations, files must be removed (pbowyer) -* `#6288 `_ Update factories.rst (velikanov) -* `#6278 `_ [HttpFoundation] Fix typo for ParameterBag getters (rendler-denis) -* `#6280 `_ Fix syntax of Company class example (cakper) -* `#6284 `_ [Book] [Routing] Fix third param true to UrlGeneratorInterface::ABSOLUTE_URI (eriwin) -* `#6269 `_ [Cookbook][Bundles]fix yaml syntax (mhor) -* `#6277 `_ remove dot in front of colon (xabbuh) -* `#6255 `_ [Cookbook][Doctrine] some tweaks to the Doctrine registration article (xabbuh) -* `#6229 `_ Rewrite EventDispatcher introduction (WouterJ) -* `#6260 `_ add missing options `choice_value`, `choice_name` and `choice_attr` to `EntityType` (HeahDude) -* `#6262 `_ [Form] reorder options in choice types references (HeahDude) -* `#6256 `_ Fixed code example (twifty) -* `#6257 `_ [Components][Form] remove outdated caution (xabbuh) -* `#6253 `_ [Security] Include guard firewall configuration sample. (calinpristavu) -* `#6250 `_ [Cookbook][Console] remove note about memory spool handling on CLI (xabbuh) -* `#6249 `_ [Cookbook][Serializer] fix wording (xabbuh) -* `#6242 `_ Removed all 2.x versionadded directives (WouterJ) -* `#6246 `_ removed duplicate lines (seferov) -* `#6222 `_ Updated "Learn more from the Cookbook" section (sfdumi) -* `#6245 `_ [Cookbook][Console] change API doc class name (xabbuh) -* `#6223 `_ Improveme the apache/mod_php configuration example (gnat42) -* `#6234 `_ File System Security Issue in Custom Auth Article (finished) (mattjanssen, WouterJ) -* `#4773 `_ [Cookbook] Make registration_form follow best practices (xelaris) -* `#6090 `_ Reworded the article about profiler storage (xavierleune, javiereguiluz) -* `#5630 `_ Add a caution about logout when using http-basic authenticated firewall (rmed19) -* `#6215 `_ Added a caution about failing cache warmers (javiereguiluz) -* `#6239 `_ Remove app_dev as build-in server is used (rmed19, WouterJ) -* `#6241 `_ [ExpressionLanguage] Add caution about backslash handling (zerustech, WouterJ) -* `#6235 `_ #6232 update forms as services section (backbone87) -* `#6236 `_ fix some minor typos (xabbuh) -* `#6237 `_ use literals for external class names (xabbuh) -* `#6206 `_ add separate placeholder examples for birthday, datetime and time type (snoek09) -* `#6238 `_ fix directive name (xabbuh) -* `#6224 `_ Note to create a service if you extend ExceptionController (pamuche) -* `#5958 `_ Update security.rst (mpaquet) -* `#6092 `_ Updated information about testing code coverage. (roga) -* `#6051 `_ Mention HautelookAliceBundle in best practices (theofidry, WouterJ) -* `#6044 `_ Added note about the hash_equals polyfill (WouterJ) -* `#6213 `_ Update form_collections.rst (insekticid) -* `#6220 `_ [book] fixes typo about redirect status codes in the controller chapter. (hhamon) -* `#6227 `_ Update testing.rst (dvapelnik) -* `#6228 `_ removed unnecessary exception from repository (gondo) -* `#6212 `_ Typo in default session save_path (DerekRoth) -* `#6208 `_ Replace references of PSR-0 with PSR-4 (opdavies) -* `#6170 `_ change translation getMessages() to getCatalogue() (snoek09) -* `#6211 `_ Remove 2.3.\* from composer snippets in the form component doc (Nicofuma) -* `#6225 `_ [Reference][Forms] add versionadded directive for range type (xabbuh) -* `#6190 `_ Fix redundant command line sample (sylozof) - -January, 2016 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#6174 `_ Missing reference docs for kernel.finish_request event (acrobat) -* `#6184 `_ added Javier as a merger for the WebProfiler bundle (fabpot) -* `#6136 `_ Update directory permissions to make var/ writable (andrerom) -* `#5600 `_ [DependencyInjection] Documented the ability of define the service decoration priority (dosten) -* `#5303 `_ [WIP] 4373 - document security events (kevintweber) -* `#6023 `_ clarify the routing component documentation a bit (dbu) -* `#6091 `_ Added an example for info() method (javiereguiluz) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6193 `_ Added the missing namespace in example of a subscriber class (raulconti) -* `#6152 `_ csrf_token_generator and csrf_token_id documentation (Raistlfiren, Aaron Valandra, xabbuh) -* `#6115 `_ [Form] Fix syntax error in code snippet (valisj) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6199 `_ fix types (garak) -* `#6207 `_ revert form login CSRF changes on wrong branch (xabbuh) -* `#6191 `_ Document the invalidate_session option (javiereguiluz) -* `#6204 `_ Expected: semicolon (cn007b) -* `#6141 `_ Docs do not match functionality (Loupax) -* `#6192 `_ fix MongoDB shell syntax highlighting (xabbuh) -* `#6147 `_ Update templating.rst - Asset absolute url fix (gbalcewicz) -* `#6187 `_ Typofix for "Defining and Processing Configuration Values" (kix) -* `#6182 `_ Latest demo has no bin folder (Jace25) -* `#6183 `_ use valid XML in code block (xabbuh) -* `#6180 `_ use single quotes for YAML strings (snoek09) -* `#6070 `_ Typo in When Things Get More Advanced (BallisticPain) -* `#6119 `_ Remove phrase "in order" (micheal) -* `#6160 `_ remove versionadded for unmaintained versions (xabbuh) -* `#6161 `_ [Contributing][Documentation] replace EOL with EOM (xabbuh) -* `#6166 `_ Fix by_reference deprecated FormType::class (nemo-) -* `#6162 `_ [Reference] add missing version number (xabbuh) -* `#6163 `_ Remove excessive pluses (aivus) -* `#6082 `_ change app/check.php for 3.0 (nanocom) -* `#6149 `_ Reference to session + Corrected sample code char (sfdumi) -* `#6130 `_ [Security][Guard] Completed start method signature (jeremyFreeAgent) -* `#6080 `_ Removed doc about getting original parameter value from ParameterBag (edefimov) -* `#6158 `_ Update override_dir_structure.rst (denniskoenigComparon) -* `#6122 `_ Added missing mandatory parameter (yceruto) -* `#6108 `_ [Form] remove the getName() function as it is deprecated (aybbou) -* `#6100 `_ [Cookbook][Security] add back updateUserSecurityIdentity() hint (xabbuh) -* `#6129 `_ Added new links to the Symfony screencast series at KnpU (javiereguiluz) -* `#6138 `_ Correction needed (pfleu) -* `#6139 `_ Update the doc to change a deprecated use case (ChristopheBoucaut) -* `#6133 `_ fixed the component name (fabpot) -* `#6127 `_ escape namespace backslashes in class role (xabbuh) -* `#5818 `_ document old way of checking validity of CSRF token (snoek09) -* `#6062 `_ Update HTTP method requirement example (WouterJ) -* `#6120 `_ fix README requirements link (garak) -* `#6109 `_ add link to Monolog configuration (snoek09) -* `#6121 `_ [MicroKernel] Fixed the display of a code block (jeremyFreeAgent) -* `#6096 `_ [Contributing] update year in license (xabbuh) -* `#6114 `_ make method protected (OskarStark) -* `#6111 `_ Fixed a typo in the choice_label code example (ferdynator) -* `#6110 `_ [Security][Guard] Fixed a typo (jeremyFreeAgent) -* `#6105 `_ Removed deprecated methods from VoterInterface (edefimov) -* `#6106 `_ Remove repetition in text (dominikhajduk) -* `#6102 `_ promoted xabbuh as merger on the Yaml component (fabpot) -* `#6014 `_ [2.8][Form] entry_type option: replace "in favor" misuses (ogizanagi) -* `#6013 `_ [2.7][Form] placeholder option: replace "in favor" misuses (ogizanagi) - -December, 2015 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5906 `_ Added documentation for choice_translation_domain option (peterrehm) -* `#6017 `_ Documented the Symfony Console Styles (javiereguiluz) -* `#5811 `_ Conversion from mysql to PDO (iqbalmalik89) -* `#5966 `_ Remove deprecated StringUtils from WSSE custom auth provider (pimpreneil) -* `#5962 `_ Simplify code example in "Adding custom extensions" section (snoek09) -* `#5977 `_ RequestStack parameter is required since 3.0 (leunggamciu) -* `#6022 `_ clarify custom route loader documentation (dbu) -* `#5994 `_ Updated the release process for Symfony 3.x and future releases (javiereguiluz) -* `#5954 `_ Fix #5236 [2.8][Translation] specify additional translation loading paths (Pierre Maraitre, Balamung) -* `#5948 `_ Update 3.0 docs accordingly to min PHP version requirement (ogizanagi) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#6086 `_ Update form_customization.rst (vudaltsov) -* `#6063 `_ minor #5829 Fix broken composer command (JHGitty) -* `#5904 `_ Update php_soap_extension.rst (xDaizu) -* `#5819 `_ Remove AppBundle (roukmoute) -* `#6001 `_ Fix class name (BlueM) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#6043 `_ Mention commiting only bower.json (krike, WouterJ) -* `#5848 `_ Added hints to spool config section (martinczerwi) -* `#5586 `_ [2.8] Remove 2.6 versionaddeds as version reached eom (WouterJ) -* `#6042 `_ some tweaks to unit testing form types (xabbuh) -* `#6059 `_ Add best practice about the Form type namespace (WouterJ) -* `#6068 `_ Remove references to API tagging (dunglas) -* `#6088 `_ Update validation.rst (syedi) -* `#6085 `_ Update validation.rst (syedi) -* `#6094 `_ [Form] Added a missing php opening tag (dev-symfony-void) -* `#5840 `_ [Contributing] [Standards] Add note about ``trigger_error()`` and deprecation messages (phansys) -* `#6050 `_ Lots of minor fixes & applying best practices to form cookbook doc (ThomasLandauer, WouterJ) -* `#5993 `_ [Cookbook] [Security] Use UserLoaderInterface instead of UserProviderInterface (ogizanagi) -* `#6071 `_ Fix syntax (WouterJ) -* `#5570 `_ Quick review of 'create framework' tutorial (WouterJ) -* `#5445 `_ Reworded the explanation about the kernel.event_listener tag (javiereguiluz) -* `#6054 `_ Remove 2.8 branch from patch documentation (Triiistan) -* `#6057 `_ Fix PHP code for registering service (WouterJ) -* `#6066 `_ Update location of ``app/check.php`` to ``bin/symfony_requirements`` (Kevinrob) -* `#6067 `_ improve phrasing (greg0ire) -* `#6063 `_ minor #5829 Fix broken composer command (JHGitty) -* `#6041 `_ Fixed misspelling of human in glossary.rst YAML (Wasserschlange) -* `#6049 `_ Finish #5798 Add ``app_`` prefix to form type names (OskarStark, WouterJ) -* `#5829 `_ use composer command instead of editing json file (OskarStark) -* `#6046 `_ Update framework.rst (typo in sesssion) (patrick-mota) -* `#5662 `_ Fixed wrong version of symfony with composer install (Nek-) -* `#5890 `_ Updated article for modern Symfony practices and the use of bcrypt (javiereguiluz) -* `#6015 `_ [Assetic] complete XML configuration examples (xabbuh) -* `#5963 `_ Add note about 'phar extension' dependency (snoek09) -* `#6006 `_ [Book] use AppBundle examples and follow best practices (xabbuh) -* `#6016 `_ Corrected the line references for the basic controller example (theTeddyBear) -* `#5446 `_ [Contributing] [Standards] Added note about phpdoc_separation (phansys) -* `#6027 `_ Update guard-authentication.rst (rvanginneken) -* `#6025 `_ Update guard-authentication.rst (rvanginneken) -* `#6038 `_ Fix #6037 (zsturgess) -* `#5820 `_ Fixed an issue with command option shortcuts (javiereguiluz) -* `#6033 `_ Fix Typo (Shine-neko) -* `#6011 `_ Fixed formatting issues (javiereguiluz) -* `#6012 `_ Use HTTPS for downloading the Symfony Installer (javiereguiluz) -* `#6009 `_ Fix missing constant usage for generating urls (Tobion) -* `#5965 `_ Removing php opening tags (Deamon) -* `#6003 `_ #5999 fix files names (vincentaubert) -* `#6004 `_ Fix for small typo (djoos) -* `#5996 `_ Clarify example for SUBMIT form event (bkosborne) -* `#6000 `_ Update registration_form.rst (afurculita) -* `#5989 `_ Fix words according context (richardpq) -* `#5992 `_ More use single quotes for YAML strings (snoek09) -* `#5957 `_ mark deep option as deprecated (snoek09) -* `#5940 `_ [Cookbook][ServiceContainer] move filename comment to the top of the code block (xabbuh) -* `#5943 `_ Add tip for when returning ``null`` from ``createToken()`` (jeroenseegers) -* `#5956 `_ Update security.rst (mpaquet) -* `#5959 `_ Fix #5912 Ambiguity on Access Decision Manager's Strategy (Pierre Maraitre) -* `#5955 `_ use single quotes for YAML strings (snoek09) -* `#5979 `_ [Book] Do not extend the base controller before introducing it (ogizanagi) -* `#5970 `_ Remove isSubmitted call (DanielSiepmann) -* `#5972 `_ Add isSubmitted call (DanielSiepmann) -* `#5964 `_ Missing n in Column (joshuataylor) -* `#5961 `_ update from_flat_php_to_symfony2.rst (thao-witkam) -* `#5924 `_ Removed note about removed content (WouterJ) -* `#5938 `_ Add proper use of the password type (themccallister) - -November, 2015 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5917 `_ [3.0][Cookbook] Use the 3.0 directory structure (WouterJ) -* `#5916 `_ [3.0][Best Practices][Quick Tour] Use the 3.0 directory structure (WouterJ) -* `#5913 `_ [3.0][Book] Use the 3.0 directory structure (WouterJ) -* `#5907 `_ Updating some places to use the new CustomUserMessageAuthenticationException (weaverryan) -* `#5922 `_ Added minimal cookbook article about the shared flag (WouterJ) -* `#5908 `_ Voter update (weaverryan) -* `#5909 `_ More 2.8 form updates (weaverryan) -* `#5927 `_ Use path() and url() PHP templating helpers (WouterJ) -* `#5926 `_ Update voter section of best practices (WouterJ) -* `#5921 `_ [2.8] Document some Security changes (WouterJ) -* `#5834 `_ Updated form aliases to FQCNs for forms in book and component (hiddewie) -* `#5265 `_ Documentation for the new Guard authentication style (weaverryan) -* `#5899 `_ Adding the MicroKernel article (weaverryan) -* `#5893 `_ Added a note about the use of _format query parameter (javiereguiluz) -* `#5891 `_ Removed the comments about the is_granted() issues in non-secure pages (javiereguiluz) -* `#5876 `_ Symfony 2.7 Form choice option update (aivus, althaus, weaverryan) -* `#5861 `_ Updated Table Console helper for spanning cols and rows (hiddewie) -* `#5835 `_ Updated CssSelector code example to use the new Converter (hiddewie) -* `#5816 `_ Merge branches (nicolas-grekas, snoek09, WouterJ, xabbuh) -* `#5804 `_ Added documentation for dnsMessage option (BenjaminPaap) -* `#5774 `_ Show a more real example in data collectors doc (WouterJ) -* `#5735 `_ [Contributing][Code] do not distinguish regular classes and API classes (xabbuh) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5903 `_ Update front controller (nurolopher) -* `#5768 `_ Removed "http_basic" config from the login form cookbook (javiereguiluz) -* `#5863 `_ Correct useAttributeAsKey usage (danrot) -* `#5833 `_ Fixed whitelist delivery of swiftmailer (hiddewie) -* `#5815 `_ fix constraint names (xabbuh) -* `#5793 `_ Callback Validation Constraint: Remove reference to deprecated option (ceithir) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5931 `_ [#5875] Fixed link description, list of common media types (Douglas Naphas) -* `#5923 `_ Remove information about request service deps of core services (WouterJ) -* `#5911 `_ Wrap all strings containing @ in quotes in Yaml (WouterJ) -* `#5889 `_ Always use "main" as the default firewall name (to match Symfony Standard Edition) (javiereguiluz) -* `#5888 `_ Removed the use of ContainerAware class (javiereguiluz) -* `#5625 `_ Tell about SYMFONY__TEMPLATING__HELPER__CODE__FILE_LINK_FORMAT (nicolas-grekas) -* `#5896 `_ [Book][Templating] Update absolute URL asset to match 2.7 (lemoinem) -* `#5828 `_ move the getEntityManager, only get it if needed (OskarStark) -* `#5900 `_ Added new security advisories to the docs (fabpot) -* `#5897 `_ Fixed some wrong line number references in doctrine.rst (DigNative) -* `#5895 `_ Update debug_formatter.rst (strannik-06) -* `#5883 `_ Book: Update Service Container Documentation (zanderbaldwin) -* `#5868 `_ [2.8] Make screenshots with the new profiler/web dev toolbar design (WouterJ) -* `#5862 `_ Fixes done automatically by the docbot (WouterJ) -* `#5851 `_ updated sentence (OskarStark) -* `#5870 `_ Update securing_services.rst (aruku) -* `#5859 `_ Use Twig highlighter instead of Jinja (WouterJ) -* `#5866 `_ Fixed little typo with a twig example (artf) -* `#5849 `_ Clarified ambiguous wording (ThomasLandauer) -* `#5826 `_ "setup" is a noun or adjective, "set up" is the verb (carlos-granados) -* `#5816 `_ Merge branches (nicolas-grekas, snoek09, WouterJ, xabbuh) -* `#5813 `_ use constants to choose generated URL type (xabbuh) -* `#5808 `_ Reworded the explanation about flash messages (javiereguiluz) -* `#5809 `_ Minor fix (javiereguiluz) -* `#5807 `_ Minor rewordings for the "deprecated" service option (javiereguiluz) -* `#5805 `_ Mentioned the BETA and RC support for the Symfony Installer (javiereguiluz) -* `#5781 `_ Added annotations example to Linking to Pages examples (carlos-granados) -* `#5780 `_ Clarify when we are talking about PHP and Twig (carlos-granados) -* `#5767 `_ [Cookbook][Security] clarify description of the getPosition() method (xabbuh) -* `#5731 `_ [Cookbook][Security] update versionadded directive to match the content (xabbuh) -* `#5681 `_ Update storage.rst (jls2933) -* `#5363 `_ Added description on how to enable the security:check command through… (bizmate) -* `#5841 `_ [Cookbook][Psr7] fix zend-diactoros Packagist link (xabbuh) -* `#5850 `_ Fixed typo (tobiassjosten) -* `#5852 `_ Fix doc for 2.6+, `server:start` replace `...:run` (Kevinrob) -* `#5837 `_ Corrected link to ConEmu (dritter) - -October, 2015 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5345 `_ Adding information about empty files sent using BinaryFileResponse. (kherge) -* `#5214 `_ [WIP] Reworking most of the registration form: (weaverryan) -* `#5051 `_ Rename CollectionType entry options (WouterJ) -* `#5677 `_ replacing deprecated usage of True, False, Null validators in docs (Tim Stamp) -* `#5314 `_ Documented the useAttributeAsKey() method (javiereguiluz) -* `#5377 `_ Added a cookbook section about event subscribers (beni0888, javiereguiluz) -* `#5623 `_ [Validator] added BIC validator (mvhirsch) -* `#5689 `_ [DI] Add some documentation for the deprecation feature (Taluu) -* `#5592 `_ Updated the article about data collectors (javiereguiluz) -* `#5745 `_ [Translation] Ability to format a message catalogue without actually writing it. (aitboudad) -* `#5702 `_ Added a reference to the Foundation form theme (totophe) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5795 `_ Fix typo in UserType class (Dorozhko-Anton) -* `#5758 `_ symlink issues with php-fpm (kendrick-k) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5843 `_ Fixed the YAML syntax for service references (javiereguiluz) -* `#5797 `_ [Process] use ProcessFailedException instead of RuntimeException. (aitboudad) -* `#5812 `_ Remove duplicate and confusing info about testing error pages (carlos-granados) -* `#5821 `_ Minor fixes in the HttpFoundation introduction article (javiereguiluz) -* `#5822 `_ Fixed a syntax issue (javiereguiluz) -* `#5817 `_ fix version for `entry_options` and `entry_type` (craue) -* `#5796 `_ Fix for #5783 (BenjaminPaap) -* `#5810 `_ Fixed a typo (javiereguiluz) -* `#5784 `_ Add fe80::1 (j-d) -* `#5799 `_ make file path consitent with other articles (OskarStark) -* `#5794 `_ Minor tweaks for the registration form article (javiereguiluz) -* `#5801 `_ namespace fix (OskarStark) -* `#5792 `_ [Cookbook][EventDispatcher] fix build (xabbuh) -* `#5787 `_ Definition Tweaks - see #5314 (weaverryan) -* `#5777 `_ Update links (thewilkybarkid) -* `#5775 `_ Misspelling (carlos-granados) -* `#5664 `_ Info about implicit session start (ThomasLandauer) -* `#5744 `_ translations have been removed from symfony.com (xabbuh) -* `#5771 `_ Remove not existing response constant (amansilla) -* `#5761 `_ [DX] [Security] Renamed key to secret (SongoQ) -* `#5766 `_ Fixed two typos (ThomasLandauer) -* `#5733 `_ [Components][OptionsResolver] adding type hint to normalizer callback (xabbuh) -* `#5561 `_ Change default value of cookie_httponly (jderusse) -* `#5678 `_ Update HttpFoundation note after recent changes in routing component (senkal) -* `#5643 `_ Document how to customize the prototype (daFish, WouterJ) -* `#5584 `_ Add DebugBundle config reference (WouterJ) -* `#5753 `_ configureOptions(...) : protected => public (lucascherifi) -* `#5750 `_ fix YAML syntax highlighting (xabbuh) -* `#5749 `_ complete Swiftmailer XML examples (xabbuh) -* `#5730 `_ Remove documentation of deprecated console shell (Tobion) -* `#5726 `_ Document the support of Mintty for colors (stof) -* `#5708 `_ Added caution to call createView after handleRequest (WouterJ) -* `#5640 `_ Update controller.rst clarifying automatic deletion for flash messages (miguelvilata) -* `#5578 `_ Add supported branches in platform.sh section (WouterJ) -* `#5468 `_ [Cookbook][Templating] Add note about cache warming namespaced twig templates (kbond) -* `#5684 `_ Fix delivery_whitelist regex (gonzalovilaseca) -* `#5742 `_ incorrect: severity is an array key here and not a constant (lbayerl) - -September, 2015 ---------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5555 `_ added result yaml and xml from example code (OskarStark) -* `#5631 `_ Updated the Quick Tour to the latest changes introduced by Symfony (javiereguiluz) -* `#5497 `_ Simplified the Quick tour explanation about Symfony Installation (DQNEO) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5629 `_ Fixing web user permission (BenoitLeveque) -* `#5673 `_ Update http_cache.rst (szyszka90) -* `#5666 `_ Fix EntityManager namespace (JhonnyL) -* `#5656 `_ Fix monolog line formatter in logging cookbook example. (vmarquez) -* `#5507 `_ Path fixed (carlosreig) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5740 `_ Fix typo in PdoSessionHandler Documentation (tobemedia) -* `#5719 `_ changed repo names to the new ones (fabpot) -* `#5227 `_ [Cookbook] Fix doc on Generic Form Type Extensions (lemoinem) -* `#5703 `_ comment old logic (OskarStark) -* `#5683 `_ Improve the demo-warning. (GuGuss) -* `#5690 `_ Updated the release process image (javiereguiluz) -* `#5188 `_ Updated Cookies & Caching section (lukey78) -* `#5710 `_ Fix grammar mistake in security.rst (zatikbalazs) -* `#5706 `_ Update assetic.rst (Acinonux) -* `#5705 `_ Update assetic.rst (Acinonux) -* `#5685 `_ Fix indentation in some annotations (iamdto) -* `#5704 `_ Fix typo in translation.rst (zatikbalazs) -* `#5701 `_ Update testing.rst (hansallis) -* `#5711 `_ removed service call from controller (sloba88) -* `#5692 `_ Made a sentence slightly more english (GTheron) -* `#5715 `_ Add missing code tag (zatikbalazs) -* `#5720 `_ adding closing tag (InfoTracer) -* `#5714 `_ Remove unnecessary word from http_cache.rst (zatikbalazs) -* `#5680 `_ fix grammar mistake (greg0ire) -* `#5682 `_ Fix grammar and CS (iamdto) -* `#5652 `_ Do not use dynamic REQUEST_URI from $_SERVER as base url (senkal) -* `#5654 `_ Doc about new way of running tests (nicolas-grekas) -* `#5598 `_ [Cookbook][Security] proofread comments in voter article (xabbuh) -* `#5560 `_ [2.3] [Contributing] [CS] Added missing docblocks in code snippet (phansys) -* `#5674 `_ Update cookbook entries with best practices (JhonnyL) -* `#5675 `_ [Contributing] add a link to the testing section (xabbuh) -* `#5669 `_ Better explanation of implicit exception response status code (hvt) -* `#5651 `_ [Reference][Constraints] follow best practices in the constraints reference (xabbuh) -* `#5648 `_ Minor fixes for the QuestionHelper documentation (javiereguiluz) -* `#5641 `_ Move important information out of versionadded (WouterJ) -* `#5619 `_ Remove a caution note about StringUtils::equals() which is no longer true (javiereguiluz) -* `#5571 `_ Some small fixes for upload files article (WouterJ) -* `#5660 `_ Improved "Community Reviews" page (webmozart) - -August, 2015 ------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5480 `_ Added page "Community Reviews" (webmozart) -* `#5595 `_ Improve humanize filter documentation (bocharsky-bw) -* `#5319 `_ [Console] Command Lifecycle explications (94noni) -* `#5394 `_ Fix Major upgrade article for 2.7.1 changes (WouterJ) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5589 `_ [Cookbook][Session] fix default expiry field name (xabbuh) -* `#5607 `_ Fix (sebastianbergmann) -* `#5608 `_ updated validation.rst (issei-m) -* `#5449 `_ Ensure that the entity is updated. (yceruto) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5553 `_ Fix all broken links/permanent redirects/removed anchors (WouterJ) -* `#5650 `_ [RFR] fixing typo and removing duplicated lines in Config component doc (salahm) -* `#5635 `_ Fix minor problems in book/page_creation.rst (fabschurt) -* `#5579 `_ [3.0] Remove mentions of Symfony1 (WouterJ) -* `#5647 `_ don't ignore the _exts directory anymore (xabbuh) -* `#5587 `_ [2.6] Don't use deprecated features (WouterJ) -* `#5637 `_ Add QueryBuilder vs DQL section (bocharsky-bw) -* `#5645 `_ Updated Constraint reference with best practices (WouterJ) -* `#5646 `_ Moved comment to the right place (mickaelandrieu) -* `#5649 `_ [RFR] Fixing typo in Symfony version for ButtonType (salahm) -* `#5606 `_ Use symfony.com theme on Platform.sh builds (WouterJ) -* `#5644 `_ Update page_creation.rst (jeromenadaud) -* `#5593 `_ Updated the profiler matchers article (javiereguiluz) -* `#5522 `_ [create_framework] Add missing extract() 2nd arg (kenjis) -* `#5597 `_ [CreateFramework] don't override existing variables (xabbuh) -* `#5628 `_ Updated the installation chapter (javiereguiluz) -* `#5638 `_ Update page_creation.rst (jeromenadaud) -* `#5636 `_ Fixed typo in web-assets.rst (nielsvermaut) -* `#5633 `_ Upgrade Platform.sh configuration snippet. (GuGuss) -* `#5620 `_ Changed the recommendation about the LICENSE file for third-party bundles (javiereguiluz) -* `#5617 `_ Add Body tag to see the web debug toolbar (rmed19) -* `#5594 `_ Missing --no-interaction flag? (alexwybraniec) -* `#5613 `_ Remove unneeded backtick (fabschurt) -* `#5622 `_ typo fix in pre authenticated (Maxime Douailin) -* `#5624 `_ the_architecture: Fix syntax error (kainjow) -* `#5609 `_ Add a missing backtick (fabschurt) -* `#5312 `_ Some fixes for bundle best practices (WouterJ) -* `#5601 `_ Update lazy_services.rst (baziak3) -* `#5591 `_ Update templating.rst: lint:twig instead of twig:lint in 2.7 (alexwybraniec) - -July, 2015 ----------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5374 `_ Remove deprecated parameters (norkunas) -* `#5533 `_ Replace Capifony with Capistrano/symfony (mojzis) -* `#5543 `_ Add deprecation notice to "choice_list" option of ChoiceType (XitasoChris) -* `#5521 `_ [Cookbook][WebServer] #5504 add a tip for the --force option (vincentaubert) -* `#5516 `_ Added a note about session data size in PdoSessionHandler (javiereguiluz) -* `#5499 `_ The "property" option of DoctrineType was deprecated. (XWB) -* `#5491 `_ added composer info (OskarStark) -* `#5478 `_ Add cookbook article for using MongoDB to store session data (stevenmusumeche) -* `#5472 `_ Added a tip about hashing the result of nextBytes() (javiereguiluz) -* `#5458 `_ HTML5 range documentation (harikt) -* `#5453 `_ Cleanup security voters cookbook recipes (WouterJ) -* `#5444 `_ Documented the "auto_alias" feature (javiereguiluz) -* `#5201 `_ [Book][Routing] Add example about how to match multiple methods (xelaris) -* `#5430 `_ Pr/5085 (sjagr, javiereguiluz) -* `#5456 `_ Completely re-reading the data transformers chapter (weaverryan) -* `#5426 `_ Documented the checkDNS option of the Url validator (saro0h, javiereguiluz) -* `#5333 `_ [FrameworkBundle] Update serializer configuration reference (dunglas) -* `#5424 `_ Integrate the "Create your own framework" tutorial (fabpot, lyrixx, jdreesen, catchamonkey, gnugat, andreia, Arnaud Kleinpeter, willdurand, amitayh, nanocom, hrbonz, Pedro Gimenez, ubick, dirkaholic, bamarni, revollat, javiereguiluz) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5567 `_ Change Sql Field name because it's reserved (rmed19) -* `#5528 `_ [reate_framework] Fix mock $matcher (kenjis) -* `#5501 `_ Fix typo in url for PHPUnit test coverage report (TrueGit) -* `#5501 `_ Fix typo in url for PHPUnit test coverage report (TrueGit) -* `#5461 `_ Rework quick tour big picture (smatejic, DQNEO, xabbuh) -* `#5488 `_ fix #5487 (emillosanti) -* `#5496 `_ Security voters fixes (german.bortoli) -* `#5424 `_ Integrate the "Create your own framework" tutorial (fabpot, lyrixx, jdreesen, catchamonkey, gnugat, andreia, Arnaud Kleinpeter, willdurand, amitayh, nanocom, hrbonz, Pedro Gimenez, ubick, dirkaholic, bamarni, revollat, javiereguiluz) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5575 `_ Move some articles from wrong sections (sylvaincombes, WouterJ) -* `#5580 `_ Additional User check in voter class (weaverryan) -* `#5573 `_ fix YAML syntax highlighting (xabbuh) -* `#5564 `_ Improve and simplify the contributing instructions about tests (javiereguiluz) -* `#5498 `_ [WIP] Added caution notes about the deprecation of container scopes (javiereguiluz) -* `#5550 `_ [docbot] Reviewed some component chapters (WouterJ) -* `#5556 `_ Fix typo Esi in part create framework (nicolasdewez) -* `#5568 `_ [Create Framework] Fix extract calls (replaces #5522) (kenjis) -* `#5548 `_ use the include() Twig function instead of the tag (xabbuh) -* `#5542 `_ [Cookbook][Email] add missing versionadded directive (xabbuh) -* `#5476 `_ [Cookbook][Security] some additional tweaks for the voter cookbook (xabbuh) -* `#5413 `_ Fix doc about deprecations policy (nicolas-grekas) -* `#5557 `_ [2.3] [Contributing] Added note about empty returns (phansys) -* `#5492 `_ updated tree for front controller (OskarStark) -* `#5536 `_ Removed reference to remove HTTPS off from nginx configuration (wjzijderveld) -* `#5545 `_ Misc. improvements in the Console component introduction (javiereguiluz) -* `#5512 `_ [Cookbook] Backport PSR-7 bridge docs to 2.3 (dunglas, weaverryan) -* `#5494 `_ updated tree (OskarStark) -* `#5490 `_ changed headline (OskarStark) -* `#5479 `_ Update http-foundation.rst (jezemery) -* `#5552 `_ rename $input to $greetInput (Xosofox) -* `#5544 `_ [components][expression_language] Fix the wrong constructor for SerializedParsedExpression (zerustech) -* `#5537 `_ Update design patter of Event Dispatcher (almacbe) -* `#5546 `_ A bunch of doc fixes again (WouterJ) -* `#5486 `_ review all Security code blocks (xabbuh) -* `#5538 `_ Update email.rst (TisLars) -* `#5529 `_ [Cookbook][upload_file] Fix :methods: to remove doubled braces (bicpi) -* `#5455 `_ Improve travis build speed (WouterJ) -* `#5442 `_ Improved the explanation about the verbosity levels of the console (javiereguiluz) -* `#5523 `_ Custom voter example, fix missing curly brace (snroki) -* `#5524 `_ TYPO: missing closing parantheses of the array (listerical85) -* `#5519 `_ Prepare Platform.sh configuration files. (GuGuss) -* `#5443 `_ Added a note about the implementation of the verbosity semantic methods (javiereguiluz) -* `#5518 `_ Minor grammar fix. (maxolasersquad) -* `#5520 `_ Fix RST (kenjis) -* `#5429 `_ Promote Symfony's builtin serializer instead of JMS (javiereguiluz) -* `#5427 `_ Cookbook grammar and style fixes (frne, javiereguiluz) -* `#5505 `_ [Cookbook][Form] some tweaks to the data transformers chapter (xabbuh) -* `#5352 `_ Update http_fundamentals.rst (wouthoekstra) -* `#5471 `_ Updated the Symfony Versions Roadmap image (javiereguiluz) -* `#5511 `_ [HttpKernel] Fix use statement (dunglas) -* `#5510 `_ [PSR-7] Fix Diactoros link (dunglas) -* `#5506 `_ Fixes small typo in data transformers cookbook (catchamonkey) -* `#5425 `_ Added a caution note about invoking other commands (kix, javiereguiluz) -* `#5367 `_ Split Security into Authentication & Authorization (iltar) -* `#5485 `_ Fix invalid phpunit URLs (norkunas) -* `#5473 `_ --dev is default and causes a warning (DQNEO) -* `#5474 `_ typo in components/translation/instruction.rst (beesofts) - -June, 2015 ----------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5423 `_ [Security] add & update doc entries on AbstractVoter implementation (Inoryy, javiereguiluz) -* `#5409 `_ [Reference] document new Doctrine APC cache service (xabbuh) -* `#5401 `_ Added some more docs about the remember me feature (WouterJ) -* `#5384 `_ Added information about the new date handling in the comparison constraints and Range (webmozart, javiereguiluz) -* `#5382 `_ Added support for standard Forwarded header (tony-co, javiereguiluz) -* `#5361 `_ Document security.switch_user event (Rvanlaak) -* `#5332 `_ [Serializer] ObjectNormalizer, object_to_populate doc. Minor enhancements. (dunglas) -* `#5335 `_ [Serializer] Updated the cookbook. (dunglas) -* `#5313 `_ Documented the overridden form options (javiereguiluz) -* `#5360 `_ [Serializer] Array Denormalization (derrabus) -* `#5307 `_ Update data_transformers.rst (zebba) -* `#5186 `_ Added a new article about using/installing unstable Symfony versions (javiereguiluz) -* `#5166 `_ Proposed a new article about using pure PHP libraries with Assetic (javiereguiluz) -* `#5416 `_ fix for Symfony 2.7 (DQNEO) -* `#5014 `_ Updated the best practices article for reusable bundles (javiereguiluz) -* `#5435 `_ Added information about the four sub-components of Security component (javiereguiluz) -* `#5368 `_ added examples for squashing (OskarStark) -* `#5428 `_ Improved description of choice_list option (adamziel, javiereguiluz) -* `#5336 `_ Adding a paragraph about updating multiple packages during an update (weaverryan) -* `#5375 `_ Added a new cookbook about file uploading (javiereguiluz) -* `#5385 `_ Added a note about the need to require Composer's autoload file (javiereguiluz) -* `#5386 `_ Re-write of Page Creation (weaverryan) -* `#5355 `_ Added a mention to the Symfony Demo application (javiereguiluz) -* `#5331 `_ [PSR-7] Bridge documentation (dunglas) -* `#5373 `_ Added mentions to some popular (and useful) Symfony bundles (javiereguiluz) -* `#4354 `_ [WCM] Added depreciation note for the cascade_validation constraint (peterrehm) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5415 `_ Updating for AppBundle and purposefully \*not\* doing work on configure (weaverryan) -* `#5407 `_ Change PhpStormOpener to PhpStormProtocol (King2500) -* `#5450 `_ Fixing "Undefined method" error in code example (nebkam) -* `#5454 `_ Changed dump() to var_dump() (WouterJ) -* `#5417 `_ Add use statement for InputDefinition (harikt) -* `#5420 `_ Fix invalid method name (bocharsky-bw) -* `#5431 `_ Updated the code to display flash messages (aykin, javiereguiluz) -* `#5418 `_ Import Psr LogLevel (harikt) -* `#5438 `_ Fixed 404 at Configuring Sessions and Save Handlers (2.3 branch) (suzuki) -* `#5412 `_ Update serializer.rst (mantulo) -* `#5397 `_ Escape backslash in error message (WouterJ) -* `#5379 `_ [Cookbook][Console] don't use BufferedOutput on Symfony 2.3 (xabbuh) -* `#5400 `_ Fix after install URL and new photo since AcmeDemoBundle is not part … (smatejic) -* `#5350 `_ [Form][2.3] fix `validation_groups` typos (craue) -* `#5358 `_ Fix typo in description (martyshka) -* `#5356 `_ [Form] Fixed typo about _token field name for CSRF protection (JMLamodiere) -* `#5362 `_ Fix invalid endtag (norkunas) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5467 `_ use HTTPS for links to symfony.com (xabbuh) -* `#5466 `_ data transformers cookbook service definition typo (intrepion) -* `#5414 `_ Rewrite sentence about fingers crossed handler action level (snoek09) -* `#5402 `_ [Contributing] [Standards] Added entry for Yoda conditions (phansys) -* `#5369 `_ Remove the Propel book chapter and explain why we do that (javiereguiluz) -* `#5460 `_ Finish #5291: Bootstrap form theme and checkboxes (anacicconi, WouterJ) -* `#5457 `_ [Cookbook][Assetic] complete a sentence (xabbuh) -* `#5398 `_ Quick review of the remember me article (WouterJ) -* `#5399 `_ Quick review of Form login chapter (WouterJ) -* `#5403 `_ [Contributing] [Standards] Added entry for identical comparison (phansys) -* `#5392 `_ Wrap the table creation inside the class extending Command, so users … (harikt) -* `#5378 `_ [Cookbook][Controller] use the jinja lexer to render Twig code (xabbuh) -* `#5421 `_ Update the name of the branch for new BC features (Restless-ET) -* `#5441 `_ [Contributing] remove mailing list and forum references (xabbuh) -* `#5433 `_ Warn users of older PHP versions Crawler might not decode HTML entities properly (jakzal, javiereguiluz) -* `#5293 `_ [Translation] Add note about how to override translation in chi… (zebba) -* `#5290 `_ Overriding 3rd party bundles (anacicconi) -* `#5242 `_ Update load_balancer_reverse_proxy.rst (urg) -* `#5381 `_ remove Yoda condition (greg0ire) -* `#5452 `_ [#5388] change echo and print in examples (snoek09) -* `#5451 `_ [#5388] change echo and print in examples (snoek09) -* `#3782 `_ [Form] Deprecate read_only option (snoob) -* `#5432 `_ removed squashing stuff. fixes #5368 (OskarStark) -* `#5383 `_ Reword a paragraph about service configurations (richardudovich) -* `#5389 `_ Updates to security.rst (HexTitan) -* `#5408 `_ typo (larsborn) -* `#5406 `_ Update yaml_format.rst (marcel-burkhard) -* `#5396 `_ [Cookbook][Bundles] fix a typo (xabbuh) -* `#5288 `_ Constraints - empty strings and null values (anacicconi) -* `#5284 `_ Split advanced container configuration article (WouterJ) -* `#5342 `_ [Cookbook][Bundles] clarify bundle installation instructions (xabbuh) -* `#5321 `_ Use the reserved domains example.com and example.org (javiereguiluz) -* `#5095 `_ Reviewed the Bundles cookbook articles (javiereguiluz) -* `#4947 `_ [Components][ClassLoader] remove DebugClassLoader (xabbuh) -* `#5365 `_ Finish #4967: Code style standardization on form type options (mimol91) -* `#5034 `_ Update the_big_picture.rst (oldskool) -* `#5351 `_ [Finder] minor CS fix (dunglas) -* `#5344 `_ [Book] Finish #4776 and #4782 (ifdattic) -* `#5348 `_ Fix list format (bicpi) -* `#5357 `_ [Form] Replace deprecated form_enctype by form_start (JMLamodiere) -* `#5359 `_ Bumped version of proxy manager to stable release (peterrehm) - -May, 2015 ---------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -* `#5329 `_ Adding a new entry about deprecation warnings (weaverryan) -* `#4604 `_ Making the channel handler more useful by showing it on the prod environment (weaverryan) -* `#5155 `_ Documented upgrading path for a major version (WouterJ) -* `#5127 `_ [VarDumper] Add doc for assertDump\* assertions (nicolas-grekas) -* `#5137 `_ Added a note about the rotating_file monolog handler (javiereguiluz) -* `#5283 `_ [BestPractices] restructured text format for the installation instructions template (xabbuh) -* `#5298 `_ Completed framework config (WouterJ) -* `#5255 `_ [Cookbook] Use configured user provider instead of injection (mvar) -* `#5216 `_ [Cookbook] [Deployment] Added note about Nginx (phansys) -* `#5169 `_ Removed synchronized services from Symfony 2.7 docs (javiereguiluz) -* `#5117 `_ Complete review of the "Customize Error Pages" cookbook article (javiereguiluz) -* `#5115 `_ Flesh out twig-template for custom data-collector (Darien Hager) -* `#5106 `_ [VarDumper] upgrade doc to 2.7 wither interface (nicolas-grekas) -* `#4728 `_ Add Session Cache Limiting section for NativeSessionStorage (mrclay) -* `#4084 `_ [Book][Forms] describe the allow_extra_fields form option (xabbuh) -* `#5294 `_ Tweaks to bower entry - specifically committing deps (weaverryan) -* `#5062 `_ Cookbook about Command in Application with AnsiToHtml (Rvanlaak) -* `#4901 `_ Removed the Internals chapter from the Symfony book (javiereguiluz) -* `#4807 `_ [2.7] bumped min PHP version to 5.3.9 (xelaris) -* `#4790 `_ [Cookbook][Routing] Update custom_route_loader.rst (xelaris) -* `#5159 `_ Added an article explaining how to use Bower in Symfony (WouterJ) -* `#4700 `_ add informations how to create a custom doctrine mapping (timglabisch) -* `#4675 `_ [Serializer] Doc for groups support (dunglas) -* `#5164 `_ Added information about the Symfony Demo application (javiereguiluz) -* `#5100 `_ Change MySQL UTF-8 examples to use utf8mb4 (DHager, Darien Hager) -* `#5088 `_ [Cookbook] Custom compile steps on Heroku (bicpi) -* `#5005 `_ Renamed precision option to scale (WouterJ) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -* `#5324 `_ 5259 improve 'Testing Documentation' in contributing guide (snoek09) -* `#5328 `_ Update create_form_type_extension.rst (jackdelin) -* `#5305 `_ [BestPractices][Security] revert #5271 on the 2.6 branch (xabbuh) -* `#5251 `_ [Cookbook][Controller] replace docs for removed `forward()` method (xabbuh) -* `#5237 `_ Update authentication.rst (taavit) -* `#5299 `_ Command controller tweaks to #5062 (weaverryan) -* `#5297 `_ Kernel Events Proofreading after #4901 (weaverryan) -* `#5296 `_ Fix link to Zend Soap (peterkokot) -* `#5266 `_ Update heroku.rst (nickbyfleet) -* `#5270 `_ Use OptionsResolver (tacman) -* `#5271 `_ Fix nonexistent controller method (amansilla) -* `#4615 `_ Update NotBlank to reflect the actual validation (DRvanR) -* `#5249 `_ [security][form login] fix translations for the security messages. (aitboudad) -* `#5247 `_ [2.7] [Serializer] fixes the order of the Serializer constructor arguments. (hhamon) -* `#5220 `_ Fix example namespace (lepiaf) -* `#5203 `_ Order has one param without spaces (carlosbuenosvinos) -* `#4273 `_ - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* `#5343 `_ [Reference][Forms] reorder index to match the description order (xabbuh) -* `#5309 `_ [Cookbook][Controller] few tweaks to the error pages article (xabbuh) -* `#5311 `_ Moved sections to be equal to index list (WouterJ) -* `#5326 `_ Fixed code intentation (lyrixx) -* `#5327 `_ [Platform] Made things more obvious and copy/paste friendly (lyrixx) -* `#5338 `_ Text in index.html.twig for The Big Picture wrong (BT643) -* `#5341 `_ fixed typo and added additional hit for NullOutput() (kuldipem) -* `#5302 `_ Place DQL in front of QueryBuilder (alfonsomga) -* `#5276 `_ Better illustrate what the "user mistake" is. (diamondsea) -* `#5304 `_ Proofreading Javier's excellent updates - in some places, shortening some things (weaverryan) -* `#5263 `_ Let docbot review the form docs (WouterJ) -* `#5280 `_ Rebase #4633 (seangallavan) -* `#5241 `_ [Components][Form] apply some fixes to the Form events chapter (xabbuh) -* `#5233 `_ Improve Choice Validation Constraint Example (huebs) -* `#5228 `_ Clarify `query_builder` closure return type (kix) -* `#5165 `_ Minor changes to match the Symfony Demo reference application (javiereguiluz) -* `#5281 `_ store templates under app/Resources/views (xabbuh) -* `#5267 `_ fix infinity upper bound (xabbuh) -* `#5277 `_ always refer to getcomposer.org through HTTPS (xabbuh) -* `#4671 `_ consistent spelling (xabbuh) -* `#4255 `_ Updated autoload standard to PSR-4. (phansys) -* `#5278 `_ remove unnecessary code (karion) -* `#5262 `_ Update Routes in the Getting Started documentation (BT643) -* `#5178 `_ Usage of denyAccessUnlessGranted in the controller (94noni) -* `#5229 `_ Remove mention of \*.class parameters from conventions (jvasseur) -* `#5250 `_ [Cookbook][Logging] use straightforward instead of straigt forward (xabbuh) -* `#5257 `_ Let docbot review the constraint docs (WouterJ) -* `#5222 `_ Update service_container.rst (assoum891) -* `#5221 `_ Update Uglifyjs.rst (assoum891) -* `#5219 `_ Fix contradicting merging policy rules (lscholten) -* `#5217 `_ Update _payload-option.rst.inc (bvleur) -* `#5226 `_ Update http_cache.rst (assoum891) -* `#5238 `_ Fixed typo and removed outdated imports (nomack84) -* `#5240 `_ [Cookbook][Email] revert #4808 (xabbuh) - -April, 2015 ------------ - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `387ebc0 `_ #5109 Improved the explanation about the "secret" configuration parameter (javiereguiluz) -- `cac0a9c `_ #5207 Updated the cookbook about Composer installation (javiereguiluz) -- `b5dd5a1 `_ #5206 [Cookbook][Security] Replace deprecated csrf_provider service (xelaris) -- `99e2034 `_ #5195 Add missing caching drivers (mhor) -- `b90c7cb `_ #5078 [Cookbook] Add warning about Composer dev deps on Heroku (bicpi) -- `55730c4 `_ #5021 Explained the "Remember Me" firewall options (javiereguiluz) -- `45ba71b `_ #4811 Simplified some Symfony installation instructions (javiereguiluz) -- `c4a5661 `_ #5060 Adds note on new validation files scanned in 2.7 (GromNaN) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `6641b4b `_ #5202 added missing tab (martinbertinat) -- `49f6b2a `_ #5211 Rebase #5182 (Balamung) -- `318bb8a `_ #5187 Fixing a bad bcrypt string using http://www.bcrypt-generator.com/ (weaverryan) -- `6fb2eea `_ #5162 Fix misplelled XliffFileLoader class in the Using Domains (Nicola Pietroluongo) -- `402b586 `_ #5162 Fix misplelled XliffFileLoader class in the Using Message Domains (Nicola Pietroluongo) -- `8fc3d6c `_ #5149 Fixed loadUserByUsername method coding errors (Linas Merkevicius) -- `2a1d2bb `_ #5153 [Book] app_dev with php built-in web server (manelselles) -- `c6e6d28 `_ #5061 Trim default is false in password field (raziel057) -- `5880f38 `_ #5126 Fix a typo in ProgressBar usage example (kamazee) -- `65c1669 `_ #5124 #3412 correct overridden option name of timezone (alexandr-kalenyuk) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `0b7f89b `_ #4868 Remove horizontal scrollbar (ifdattic) -- `c166fdf `_ #5212 Fixed typo. (pcky) -- `134268e `_ #5209 [Reference] Fix order of config blocks (xelaris) -- `c6dc4ea `_ #5200 Added missing link in "Sections" (sfdumi) -- `8b25e6e `_ #5198 Link twig constant function (davidfuhr) -- `2d6d78c `_ #5194 Fix priority range values for event listeners. (chtipepere) -- `914345a `_ #5191 Fixed inconsistency (jperovic) -- `c2d1f3d `_ #5190 Change '.xliff' extensions to '.xlf' (xelaris) -- `32b874a `_ #5189 [Quick Tour] Fixed things found by the docbot (WouterJ) -- `20ac2a6 `_ #5174 [CookBook] [configuration_organization] Use $this->getRootDir() instead of __DIR__ (manelselles) -- `eacb71b `_ #5173 Use $this->getRootDir() instead of __DIR__ (manelselles) -- `16e0849 `_ #5184 Removing a section about Roles that I think has no real use-case (weaverryan) -- `2948d6e `_ #5185 Fix broken link in security chapter (iboved) -- `a4f290e `_ #5172 [Contributing][Code] add missing security advisories (xabbuh) -- `2b7ddcd `_ #5167 Add version 2.8 to the release roadmap (Maks3w) -- `404d0b3 `_ #5161 Use correct Session namespace (JhonnyL) -- `c778178 `_ #5098 Reviewed Configuration cookbook articles (javiereguiluz) -- `d9e1690 `_ #5096 Reviewed Cache cookbook articles (javiereguiluz) -- `c40b618 `_ #5065 [Reference] fix code block order (xabbuh) -- `73ccc8b `_ #5160 Update process.rst (sfdumi) -- `ab01d08 `_ #5141 Removed remaining setDefaultOptions usage (WouterJ) -- `0dc6204 `_ #5143 Rebased #4747 (ifdattic) -- `b467e23 `_ #5147 Add missing word in bundles best practice description (jbafford) -- `bf1e44b `_ #5150 [Cookbook] Update serializer.rst (xelaris) -- `bec695a `_ #5144 [Cookbook][Deployment] fix references to Platform.sh documentation (xabbuh) -- `b73346a `_ #5145 Update introduction.rst (cafferata) -- `7f39e87 `_ #5073 [Cookbook] Add note about possible 404 error on Heroku (bicpi) -- `fbdc177 `_ #5057 Add a link to Multiple User Providers (thePanz) -- `526c880 `_ #5132 [Components][DependencyInjection] fix wrong disable of factories (sstok) -- `b19ded6 `_ #5130 [Cookbook][Security] Fiyed typo in entity_provider.rst (althaus) -- `87c39b7 `_ #5129 Fix to Twig asset function packageName argument (ockcyp) -- `1d443c0 `_ #5128 [VarDumper] little optim (lyrixx) - -March, 2015 ------------ - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `25d2f54 `_ #4958 Add Twitter Bootstrap form theme example (bocharsky-bw) -- `8ac6fed `_ #5093 Added a new best practices for custom form field types (javiereguiluz) -- `50cd620 `_ #4892 Add possible values for widget_type (XitasoChris) -- `ade7ba4 `_ #4842 Add meaning of yellow icon for number of queries (XitasoChris) -- `fa10f1c `_ #5083 Proofreading and updating entity_provider for readability (weaverryan) -- `e36faec `_ #5099 Rebase of #4989 (solazs, weaverryan) -- `65dd03b `_ #5056 [Reference] Add missing option delivery_whitelist (TerjeBr) -- `c2f21e6 `_ #5050 [OptionsResolver] Fixed deprecated functionality usage (WouterJ) -- `3405c42 `_ #5046 Rebased "add shortcut methods" (Cydonia7, WouterJ) -- `b138a50 `_ #5032 Minor improvement for symfony-installer with LTS (94noni) -- `5261e79 `_ #5033 adding table for controller as a service (dbu) -- `d6c0cb7 `_ #5028 Finish #4308: Documentation for the new PropertyNormalizer (mnapoli, WouterJ) -- `ccabc95 `_ #5023 Added a note about data transformers not being applied with inherit_data option set (javiereguiluz) -- `65a33c0 `_ #5020 Added a commented config useful when you use symlinks (javiereguiluz) -- `1dbed80 `_ #5017 Added a note about the server_version DBAL option (javiereguiluz) -- `86abdde `_ #5015 Added an example about how to get the impersonating user object (javiereguiluz) -- `c6db525 `_ #5010 Added a note about the Symfony versions affected by ICU problems (javiereguiluz) -- `3c76623 `_ #5008 Added a note about how to enable http_method_override for caching kernels (javiereguiluz) -- `22eee86 `_ #4987 Added the documentation for the new Asset component (javiereguiluz) -- `3fb19ce `_ #4959 Add excluded_ajax_paths new parameter in v2.6 (bocharsky-bw) -- `78733c3 `_ #4941 Adding a section to emailing showing off absolute_url (weaverryan) -- `325354e `_ #4903 Reworded the explanation about when a lock is released (javiereguiluz) -- `d76f046 `_ #4875 Added chapter about the locale based on the user entity (peterrehm) -- `0d1e97e `_ #4834 [translator] use the new fallbacks option. (aitboudad) -- `9846d97 `_ #5001 Best practices template names (WouterJ) -- `8e93786 `_ #4779 Update book to comply with best practices, round 3 (WouterJ) -- `dbdb408 `_ #4724 [Reference][Constraints] document the validation payload option (xabbuh) -- `f8e2e19 `_ #4692 [Serializer] Name Converter (dunglas) -- `24c4f42 `_ #4732 [Reference] add missing reference options and descriptions (xabbuh) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `6ba6ffd `_ #5058 Fix: assets_version instead of asset_version (sebastianblum) -- `edf9b78 `_ #5118 Update logger.rst (jdecoster) -- `adf5b90 `_ #5110 [Serializer] Fix class name (iamluc) -- `d65880f `_ #5092 Fixed a minor error introduced by the new redirectToRoute() method (javiereguiluz) -- `206e613 `_ #4304 [DX] Suggest a hint to any auth-check (larsborn) -- `df9c3f4 `_ #5053 Correct RegisterListenersPass namespace (hacfi) -- `893ffad `_ #5041 Fixed variable name in : Reference -> validation constraints -> count -> PHP (aminemat) -- `42ba278 `_ #5037 Finish 4644: Update the_controller.rst (teggen, WouterJ) -- `e9b9376 `_ #5009 Reworded the explanation about optional command options (javiereguiluz) -- `f9901d5 `_ #5000 Fixed case where service definition is actually an alias (Xavier Coureau) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `e7cdb2b `_ #5121 As per twigphp/Twig#472, automatic escaping is not yet available (Ocramius) -- `bce3f04 `_ #5114 [Cookbook][Templating] Use best practice to put templates in app/ dir (WouterJ) -- `d43b845 `_ #5116 Fixes for 2.3 branch (ifdattic) -- `eef413b `_ #5090 Quick review of the Security book chapter (WouterJ) -- `b07c0f4 `_ #5102 Removed duplicate "long"s in length constraint (ByStones) -- `c19598a `_ #5101 [Cookbook][Deployment] some tweaks to #4601 (xabbuh) -- `7e669c1 `_ #5105 added Jakub as a merger for the DomCrawler component (fabpot) -- `2c3513e `_ #5097 added xabbuh to the list of the Symfony core team member (fabpot) -- `6b96470 `_ #5076 Better explain that form types should be unique in the application (javiereguiluz) -- `cdb9350 `_ #5086 Use AppBundle instead of AcmeDemoBundle for consistency (snamor) -- `6719802 `_ #5108 [Components][HttpKernel] fix typo in event flow diagrams (xabbuh) -- `d6a838a `_ #5082 Proofreading tweaks to asset component (weaverryan) -- `17a6863 `_ #5094 Reviewed the Assetic cookbook articles (javiereguiluz) -- `ac9ba97 `_ #4909 Remove horizontal scrollbar and other fixes (ifdattic) -- `51af15d `_ #5087 added Abdellatif as a core team member (fabpot) -- `a801d57 `_ #4601 [Heroku] A few more tweaks to outline the steps (weaverryan) -- `b76ffad `_ #4464 [BestPractices Removing micro-optimization note about @Template (weaverryan) -- `b3e204c `_ #5079 [Contributing][Code] link to deciders' GitHub profiles (xabbuh) -- `33232a8 `_ #5075 Removed an admonition that is no longer true for Symfony 2.6+ (javiereguiluz) -- `4307190 `_ #5072 Add missing use statement in Building Login Form doc (ockcyp) -- `9468b9a `_ #5071 Fixed incorrect plural form (Katharina Störmer) -- `63f1ca3 `_ #5066 [Reference] enclose data type with double backticks (xabbuh) -- `dc01076 `_ #5064 Updated documentation standards (code examples and English use) (javiereguiluz) -- `0d0c795 `_ #5047 Fix service id (JhonnyL) -- `2fe8f76 `_ #5044 Minor improvement in the node types explanation (javiereguiluz) -- `9b1f5f1 `_ #5043 Switched the first example to a static constructor method (kix) -- `ce19196 `_ #5042 added some more components for Tobion as a merger (fabpot) -- `b8a11e1 `_ #5036 A very quick reread of the Form Login article (WouterJ) -- `e94ec09 `_ #5035 reword to serves (cordoval) -- `5eb52e3 `_ #5031 Reworded the note about Windows console and output coloring (javiereguiluz) -- `df72862 `_ #5030 Finish #4586: Update routing.rst (guangle) -- `93387bf `_ #5029 Finish #4370: add a note about apc for php recent versions (ip512, WouterJ) -- `66cf990 `_ #5018 Added a note about the class option of the services defined via factories (javiereguiluz) -- `a89448d `_ #5012 #4032 improved comments about em option (raulfraile) -- `1c50386 `_ #5011 tip for mapping definition (SrgSteak) -- `ce8744d `_ #5081 [Cookbook][Session] some language tweaks (xabbuh) -- `1ee04ba `_ #5006 Added a note about log file sizes and the use of logrotate (javiereguiluz) -- `3be0081 `_ #4976 Improved sentence (edsonmedina) -- `a444220 `_ #4885 Fix typos (ifdattic) -- `482502d `_ #4793 [Contributing] Several tweaks (xelaris) -- `a2395ef `_ #5054 [Changelog] fix changelog syntax (xabbuh) -- `6b66f03 `_ #5003 Updated the generic Deployment article (javiereguiluz) -- `39a1487 `_ #4999 Fixed semantic error (beni0888) - -February, 2015 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `16dcf53 `_ #4980 [#4974] Added Twig loader priority Documentation (wizhippo) -- `a25da10 `_ #4966 [#4231] Clarify that only the main command triggers events (riperez) -- `c6bea37 `_ #4957 Added a mention to the @Security annotation (javiereguiluz) -- `9cce63c `_ #4924 [swiftmailer] Document whitelist option to email redirect (TerjeBr) -- `14a080f `_ #4907 Adjustments to PDO Session storage page (kbond) -- `f5ff45e `_ #4712 Provide full test example (ifdattic) -- `5e83045 `_ #4657 Update assetic watch command (xtreamwayz) -- `d447b12 `_ #4556 Updated twig reference with optimizations and paths (jzawadzki) -- `ed80100 `_ minor #4977 Unnecessary comma (edsonmedina) -- `018cf3f `_ #4661 Added a short cookbook about avoiding the automatic start of the sessions (javiereguiluz) -- `2305066 `_ #4902 Removed the Stable API chapter from the Symfony book (javiereguiluz) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `215cacf `_ #4423 Fix description of ConfirmationQuestion (cxj) -- `ed80100 `_ minor #4977 Unnecessary comma (edsonmedina) -- `583ec92 `_ #4984 Fix the example using SerializedParsedExpression (stof) -- `b0d9c5c `_ #4978 fix wrong header-line syntax (sstok) -- `6d65564 `_ #4954 Fixed some syntax issues in Twig Reference (javiereguiluz) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `2a29225 `_ #4985 Fixed a typo (javiereguiluz) -- `f75bc2b `_ #4972 Fix typos (ifdattic) -- `89e626f `_ #4952 symfony 2.7 requires at least php 5.3.9 (scaytrase) -- `9fab10b `_ #4854 Removed no longer needed information about PHP 5.3 (WouterJ) -- `1726054 `_ #4500 Link to standard edition (harikt) -- `91ff6f8 `_ #4329 ensure consistency with the note (greg0ire) -- `f4ab4b6 `_ #5002 Revert very bad merge (WouterJ) -- `e5dbd49 `_ #4977 Unnecessary comma (edsonmedina) -- `ed80100 `_ #4977 Unnecessary comma (edsonmedina) -- `5d44987 `_ #4991 Fixed typo and tweaked syntax. (cdvrooman) -- `b1aadbf `_ #4993 Bumped symfony version number to 2.6 in flat php composer.json example (TSchuermans) -- `3845c9c `_ #4979 require_once path fixed (mvanmeerbeck) -- `96770aa `_ #4969 Add typehint (piotrantosik) -- `f97d01f `_ #4995 [#4965] file extension fix (hansstevens) -- `c5647dd `_ #4968 Fix typo (ifdattic) -- `c3218fc `_ #4962 cookbok/security/acl.rst (DaliusK) -- `72489a4 `_ #4963 Normalize excluded_404s in monolog cookbook (jbafford) -- `0adb6f6 `_ #4964 link to the cookbook article on avoiding to start a session (dbu) -- `5d8456f `_ #4955 Fixed wrong API link (dosten) -- `0a85053 `_ #4950 Fixes for 2.3 branch (ifdattic) -- `d3d96e1 `_ #4951 fix characters in backported patch (xabbuh) -- `208904a `_ #4949 Fixes for 2.3 branch (ifdattic) -- `6be214c `_ #4948 Fixes for 2.6 branch (ifdattic) -- `42b44c4 `_ #4929 Remove block which doesn't make sense after best practices (ifdattic) -- `008c4de `_ #4928 Change installation method order (ifdattic) -- `6f8b145 `_ #4904 Added a reference about including JS and CSS files in PHP templates (javiereguiluz) - -January, 2015 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `b32accb `_ minor #4935 Fix typos (ifdattic) -- `ad74169 `_ #4628 Varnish cookbook session cookie handling (dbu) -- `50c5a9e `_ #4895 Added configuration of the user provider (peterrehm) -- `4226fc2 `_ #4883 Global dump (nicolas-grekas) -- `a57db5b `_ #4879 Documented true regex (WouterJ) -- `3bb7b61 `_ #4645 Remove note that's no longer the case (thewilkybarkid) -- `3293286 `_ #4801 [Cookbook][cache][varnish] be more precise about version differences (dbu) -- `572bf3b `_ #4800 [Cookbook][Security] Hint about createToken can return null (xelaris) -- `74d2e30 `_ #4786 Replaced setDefaultOptions by the new configureOptions method (peterrehm) -- `528e8e1 `_ #4740 Use AppBundle whenever it's possible (javiereguiluz) -- `08e5ac9 `_ #4658 Debug formatter tweaks (weaverryan) -- `cfad26c `_ #4605 Adding a link to log things in the prod environment (weaverryan) -- `3643ec2 `_ #4723 [Cookbook][Security] document the new AuthenticationUtils (xabbuh) -- `9742b92 `_ #4761 [Cookbook][Security] don't output message from AuthenticationException (xabbuh) -- `a23e7d2 `_ #4643 How to override vendor directory location (gajdaw) -- `99aca45 `_ #4749 [2.3][Book][Security] Add isPasswordValid doc as in 2.6 (xelaris) -- `d9935a3 `_ #4141 Notes about caching pages with a CSRF Form (ricardclau) -- `207f2f0 `_ #4711 [Reference] Add default_locale config description (xelaris) -- `1b0fe77 `_ #4708 Change Apache php-fpm proxy configuration (TeLiXj) -- `7be0dc6 `_ #4681 adding note to assetic cache busting (weaverryan) -- `127ebc1 `_ #4650 Documented the characters that provoke a YAML escaping string (javiereguiluz) -- `0c0b708 `_ #4454 More concrete explanation of validation groups (peterrehm) -- `4fe4f65 `_ #4682 [Reference] document the `````2.5````` validation options (xabbuh) -- `144e5af `_ #4611 Adding a guide about upgrading (weaverryan) -- `01df3e7 `_ #4626 clean up cache invalidation information on the cache chapter (dbu) -- `5f7ef85 `_ #4651 Documented the security:check command (javiereguiluz) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `ea51aeb `_ #4926 Finish #4505: Fixed composer create-project command (windows) (Epskampie) -- `b32accb `_ minor #4935 Fix typos (ifdattic) -- `7e84533 `_ #4886 [Best Pracitices] restore example in the "Service: No Class Parameter" section (u-voelkel) -- `a6b7d72 `_ #4861 Ifdattic's fixes (ifdattic) -- `8ef3477 `_ #4856 [Components][Debug] fix DebugClassLoader namespace (xabbuh) -- `b9359a2 `_ #4905 Update routing.rst (IlhamiD) -- `9fee9ee `_ #4746 Revert #4651 for 2.3 branch (xelaris) -- `5940d52 `_ #4735 [BestPractices] remove @Security annotation for Symfony 2.3 (xabbuh) -- `ce37b96 `_ #4771 [QuickTour] use the debug:router command name (xabbuh) -- `ffe3425 `_ #4765 [Book][Forms] avoid the request service where possible (xabbuh) -- `36f2e1f `_ #4757 [Components][ClassLoader] don't show deprecated usage of ``Yaml::parse()`` (xabbuh) -- `d8e8d75 `_ #4756 [Components][Config] don't show deprecated usage of ``Yaml::parse()`` (xabbuh) -- `b143754 `_ #4744 [Book][Security] Update code example to fit description (xelaris) -- `310f4ae `_ #4639 Update by_reference.rst.inc (docteurklein) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `2cff942 `_ #4878 [Book][Security] Remove out-dated anchor (xelaris) -- `a97646f `_ #4882 Remove horizontal scrollbar (ifdattic) -- `c24c787 `_ #4931 Remove horizontal scrollbar (ifdattic) -- `83696b8 `_ #4934 Fixes for 2.3 branch (ifdattic) -- `99d225b `_ #4943 Fixes for 2.3 branch (ifdattic) -- `3907af6 `_ #4944 Fix formatting (ifdattic) -- `137ba72 `_ #4945 Fixes for 2.3 branch (ifdattic) -- `5a53e87 `_ #4946 Remove horizontal scrollbar (ifdattic) -- `b32accb `_ #4935 Fix typos (ifdattic) -- `04090c0 `_ #4936 fixed typo (issei-m) -- `0fa9cbd `_ #4937 Keeping documentation consistent (thecatontheflat) -- `3921d70 `_ #4918 Quick proofread of the email cookbook (weaverryan) -- `768650e `_ #4932 Add missing comma in array (ifdattic) -- `418a73b `_ #4922 Fix typo: missing space (ifdattic) -- `30ecdde `_ #4921 Fixes for 2.5 branch (ifdattic) -- `d1103a8 `_ #4919 Fix code examples (ifdattic) -- `20d80c3 `_ #4916 Fixes for 2.3 branch (ifdattic) -- `d7acccf `_ #4914 Fix typo, remove horizontal scrollbar (ifdattic) -- `fc776ab `_ #4894 Align methods in YAML example (ifdattic) -- `bd279f6 `_ #4908 Set twig service as private (ifdattic) -- `37fd035 `_ #4899 Fix typo: looks => look (ifdattic) -- `fbaeecd `_ #4898 added Kévin Dunglas as a merger for the Serializer component (fabpot) -- `7c66a8b `_ #4893 Move annotations example to front (ifdattic) -- `2b7e5ee `_ #4891 fixed typo (acme -> app) (adiebler) -- `00981de `_ #4890 Fixed typo (beni0888) -- `dc87147 `_ #4876 Remove horizontal scrollbar (ifdattic) -- `f5f3c1b `_ #4865 Removed literals for bundle names (WouterJ) -- `33914c9 `_ #4859 [Components][EventDispatcher] don't explain deprecated `````getName()````` method (xabbuh) -- `9a6d7b9 `_ #4831 Update override.rst (ifdattic) -- `f9c2d69 `_ #4803 [Book][Translation] Added tip for routing params (xelaris) -- `2f41c9e `_ #4887 Typo (XitasoChris) -- `3774a37 `_ #4881 Remove 'acme' (ifdattic) -- `d85fa76 `_ #4880 Remove duplicate link, introduction.rst (Quberik) -- `6a15077 `_ #4874 Remove trailing whitespace (WouterJ) -- `80bef5a `_ #4873 [BestPractices] fix typo (xabbuh) -- `6cffa4e `_ #4866 Remove horizontal scrollbar (ifdattic) -- `65b0822 `_ #4798 Add version added note for the debug:event-dispatcher command (adamelso) -- `bcf1508 `_ #4785 [Book][Security] add back old anchors (xabbuh) -- `4143076 `_ #4872 [BestPractices] fix merge after removing @Security in 2.3 (xabbuh) -- `dc25c65 `_ #4769 [2.7] Removed 2.5 versionadded as its deprecated (WouterJ) -- `48835de `_ #4767 [2.6] Removed 2.4 versionadded as version is deprecated (WouterJ) -- `240a981 `_ #4764 [Reference][Forms] move cautions to make them visible (xabbuh) -- `cf3d38a `_ #4731 [Book][Testing] bump required PHPUnit version (xabbuh) -- `4f47dec `_ #4837 Monolog Cookbook Typo Fix: "allows to" should be "allows you to" (mattjanssen) -- `c454fd2 `_ #4857 Add custom link labels where Cookbook articles titles looked wrong (javiereguiluz) -- `17989fd `_ #4860 [Components][HttpKernel] replace API link for SwiftmailerBundle (xabbuh) -- `84839ba `_ #4829 Fix code example (ifdattic) -- `e347ec8 `_ #4819 Removed a leftover comma in security config sample (javiereguiluz) -- `11b9d23 `_ #4772 Tweaks to the new form csrf caching entry (weaverryan) -- `c04ed79 `_ #4848 Fix typo: BLOG => BLOB (ifdattic) -- `f9c1389 `_ #4845 Update security.rst (meelijane) -- `9680ec0 `_ #4844 Update routing.rst (bglamer) -- `c243d00 `_ #4843 Fixed typo (beni0888) -- `5b91653 `_ #4843 Fixed typo (beni0888) -- `13ffb83 `_ #4835 Fixed broken link (SofHad) -- `d2a67ac `_ #4826 Fixed 404 page (SofHad) -- `f34fc2d `_ #4825 Fixed the 404 not found error (SofHad) -- `467c538 `_ #4824 fix SQL: table names (e-moe) -- `91a89b7 `_ #4821 Fixed typo (SofHad) -- `f7179df `_ #4818 [Routing] Removed deprecated usage (WouterJ) -- `82bce29 `_ #4815 Update translation.rst (ifdattic) -- `892586b `_ #4808 Email message instantiation changed to a more 'symfonysh' way. (alebo) -- `e913808 `_ #4802 [Cookbook][Routing] Fixed typo (xelaris) -- `6522145 `_ #4799 Fix markup (WouterJ) -- `a42e5b6 `_ #4778 Update templating.rst (ifdattic) -- `bd7d246 `_ #4752 [Book][Validation] clarify group validation (xabbuh) -- `236c26f `_ #4796 Update service_container.rst (ifdattic) -- `f85c44c `_ #4795 Remove horizontal scrollbar (ifdattic) -- `45189bb `_ #4792 [BestPractices] add filename to codeblock (xelaris) -- `fccea1d `_ #4791 Fix heading level in form_login_setup.rst (xelaris) -- `74c3a35 `_ #4788 Controller is a callable (timglabisch) -- `eb56376 `_ #4781 [Serializer] Bad variable name in example (arno14) -- `28571fc `_ #4780 Add missing semicolon (NightFox7) -- `32bd0b1 `_ #4777 Update templating.rst (ifdattic) -- `dc5d8f8 `_ #4760 Update routing.rst (ifdattic) -- `4e880c1 `_ #4755 fix typo (xabbuh) -- `463c30b `_ #4751 [BestPractices] fix alignment of YAML values (xelaris) -- `1972757 `_ #4775 Corrected validation information on inheritance (peterrehm) -- `f4f8621 `_ #4762 [Cookbook][Configuration] update text to use SetHandler (not ProxyPassMatch) (xabbuh) -- `43543bb `_ #4748 Re-reading private service section (weaverryan) -- `e447e70 `_ #4743 [Book][Security] Fix typo and remove redundant sentence (xelaris) -- `97a9c7b `_ #4742 Formatting fix (WouterJ) -- `9819113 `_ #4702 Clarify tip for creating a new AppBundle (xelaris) -- `8f2fe87 `_ #4683 [Reference] update the configuration reference (xabbuh) -- `e889813 `_ #4677 Add exception to console exception log (adrienbrault) -- `9958c41 `_ #4656 Tried to clarify private services (WouterJ) -- `1d5966c `_ #4703 Fix representation (ifdattic) -- `aa9d982 `_ #4697 Set twig service as private (ifdattic) -- `ece2c81 `_ #4722 Improve readability (ifdattic) -- `dcc9516 `_ #4725 Remove horizontal scrollbar (ifdattic) -- `3eb14aa `_ #4727 Renamed example: "Acme\BlogBundle" -> "AppBundle" (muxator) -- `25dd825 `_ #4730 Fix typo: as => is (ifdattic) -- `760a441 `_ #4734 [BestPractices] add missing comma (xabbuh) -- `caa2be6 `_ #4737 [Book][Security] add missing versionadded directive (xabbuh) -- `8c1afb9 `_ #4738 [Contributing][Code] update year in license (xabbuh) -- `4ad72d0 `_ #4741 use the doc role for internal links (jms85, xabbuh) -- `57fdea6 `_ #4729 Fixed typo in factories.rst (nietonfir) - -December, 2014 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `00a13d6 `_ #4606 Completely re-reading the security book (weaverryan) -- `aa88f99 `_ #4609 Adding details about the changes to the PdoSessionHandler in 2.6 (weaverryan) -- `bd65c3c `_ #4673 [Reference] add validation config reference section (xabbuh) -- `55a32cf `_ #4173 use a global Composer installation (xabbuh) -- `c5e409b `_ #4526 Deploy Symfony application on Platform.sh. (GuGuss) -- `ddd56ea `_ #4449 Added cache_busting to default asset config (GeertDD) -- `c837ea1 `_ #4665 Documented the console environment variables (javiereguiluz) -- `0e45e29 `_ #4655 Document new progressbar methods (javiereguiluz) -- `f4a7196 `_ #4627 Rewrite the varnish cookbook article (dbu) -- `92a186d `_ #4654 Rewritten from scratch the chapter about installing Symfony (javiereguiluz) -- `90ef4ec `_ #4580 Updated installation instructions to use the new Symfony Installer (javiereguiluz) -- `f591e6e `_ #4532 GetResponse*Events stop after a response was set (Lumbendil) -- `a09fd7b `_ #4485 Added documentation about the DebugFormatter helper (WouterJ) -- `d327bae `_ #4557 Update pdo_session_storage.rst (spbentz) -- `71495e8 `_ #4528 Update web_server_configuration.rst (thePanz) -- `3b9d60d `_ #4517 [Reference] document configurable PropertyAccessor arguments (xabbuh) -- `9b330ef `_ #4507 Comply with best practices, Round 2 (WouterJ) -- `39a36bc `_ #4405 Finish 3744 (mickaelandrieu, xabbuh) -- `5363542 `_ #4188 Updated documentation regarding the SecurityContext split (iltar) -- `f30f753 `_ #4050 [Translation] added logging capability. (aitboudad) -- `db35c42 `_ #4591 Instructions for setting SYMFONY_ENV on Heroku (dzuelke) -- `8bba316 `_ #4457 [RFC] Clarification on formatting for bangs (!) (bryanagee) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `79db0b9 `_ #4699 Use new security.authorization_checker service (xelaris) -- `9c819b4 `_ #4713 [Security] Removed deprecated example about SecurityContext (iltar) -- `153565e `_ #4707 [Cookbook] Fix XML example for RTE (dunglas) -- `cad4d3f `_ #4582 Completed the needed context to successfully test commands with Helpers (peterrehm) -- `a137918 `_ #4641 Add missing autoload include in basic console application example (senkal) -- `0de8286 `_ #4513 [Contributing] update contribution guide for 2.7/3.0 (xabbuh) -- `8b611e2 `_ #4598 [ExpressionLanguage] add missing argument (xabbuh) -- `7ea4b10 `_ #4646 Update the_controller.rst (teggen) -- `a2ea256 `_ #4637 fixed StringExpressionLanguageProvider code example #4636 (danieleorler) -- `63be343 `_ #4630 [OptionsResolver] Fix namespace (xavren) -- `baf61a0 `_ #4623 [OptionsResolver] Fix Namespace link (xavren) -- `8246693 `_ #4613 Change refering block name from content to body (martin-cerny) -- `1750b9b `_ #4599 [Contributing] fix feature freeze dates (xabbuh) -- `8e2e988 `_ #4603 Replace form_enctype(form) with form_start(form). (xelaris) -- `7acf27c `_ #4552 required PHPUnit version in the docs should be updated to 4.2 (or later)... (jzawadzki) -- `df60ba7 `_ #4548 Remove ExpressionLanguage reference for 2.3 version (dangarzon) -- `727c92a `_ #4594 Missing attribute 'original' (Marcelsj) -- `97a9c43 `_ #4533 Add command to make symfony.phar executable. (xelaris) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `8bd694f `_ #4709 [Reference] fix wording (xabbuh) -- `1bd9ed4 `_ #4721 [Cookbook][Composer] fix note directive (xabbuh) -- `5055ef4 `_ #4715 Improve readability (ifdattic) -- `d3d6d22 `_ #4716 Fix typo: con => on (ifdattic) -- `afe8684 `_ #4720 Link fixed (kuldipem) -- `4b442a0 `_ #4695 Misc changes (ifdattic) -- `0db36ea `_ #4706 Fix typo: than in Twig => than Twig templates (ifdattic) -- `94b833e `_ #4679 General grammar and style fixes in the book (frne) -- `3f3464f `_ #4689 Update form_customization.rst (rodrigorigotti) -- `8d32393 `_ #4691 replace "or" with "," (timglabisch) -- `9b4d747 `_ #4670 Change PHPUnit link to avoid redirect to homepage (xelaris) -- `8ccffb0 `_ #4669 Harmonize PHPUnit version to 4.2 or above (xelaris) -- `84bf5e5 `_ #4667 Remove redundant "default" connection (xelaris) -- `ceca63f `_ #4653 update ordered list syntax (xabbuh) -- `459875b `_ #4550 Ref #3903 - Normalize methods listings (ternel) -- `87365fa `_ #4648 Update forms.rst (keefekwan) -- `70f2ae8 `_ #4640 [Book] link to the API documentation (xabbuh) -- `95fc487 `_ #4608 Removing some installation instructions (weaverryan) -- `96455e6 `_ #4539 Normalization of method listings (pedronofuentes) -- `bd44e6b `_ #4664 Spelling mistake tens to tons (albabar) -- `3b6341a `_ #4663 Removed double `````firewall_restriction````` entry (vlad-ghita) -- `815e0bf `_ #4551 Normalize the method listings on version 2.5 (pedronofuentes) -- `48cc9cd `_ #4647 Update controllers.rst (keefekwan) -- `2efed8c `_ #4660 Fix indentation of YAML example (xelaris) -- `b55ec30 `_ #4659 Fixed some code indentation (javiereguiluz) -- `18af18b `_ #4652 replace Symfony2 with Symfony (xabbuh) -- `a70c489 `_ #4649 Linked the PDO/DBAL Session article from the Doctrine category (javiereguiluz) -- `f672a66 `_ #4625 Added '-ing' title ending to unify titles look (kix) -- `9600950 `_ #4617 [Filesystem] filesystem headlines match method names (xabbuh) -- `8b006bb `_ #4607 [Best Practices] readd mistakenly removed label (xabbuh) -- `7dcce1b `_ #4585 When explaining how to install dependencies for running unit tests, (carlosbuenosvinos) -- `1c9270d `_ #4568 Update Symfony reference to 2.6 version (dangarzon) -- `33ca697 `_ #4561 Use the new build env on Travis (joshk) -- `107610e `_ #4531 [symfony] [Hackday] Fixed typos (pborreli) -- `3b1611d `_ #4519 remove service class parameters (xabbuh) -- `3bd17af `_ #4518 [Components][DependencyInjection] backport service factory improvements (xabbuh) -- `d203e5a `_ #4495 [Best Practices][Business Logic] link to a bundle's current (not master) docs (xabbuh) -- `0a9c146 `_ #4422 Fix typos in code (ifdattic) -- `4f0051d `_ #4574 fixed little typo (adridev) - -November, 2014 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `33554fc `_ #4456 New validation API usage in Class Constraint Validator (skwi) -- `135aae6 `_ #4433 Completely re-reading the controller chapter (weaverryan) -- `f748378 `_ #4498 Use new factory syntax (WouterJ) -- `59f0374 `_ #4490 Documented ExpressionLanguage extensibility (WouterJ) -- `ed241ab `_ #4487 Documented html5 option (WouterJ) -- `48a5af3 `_ #4486 Renamed empty_value to placeholder (WouterJ) -- `422e0f1 `_ #4465 Modifying the best practice to use form_start() instead of
`_ #4463 [BestPractices] Proposing that we make the service names *just* a little bit longer (weaverryan) -- `9a22865 `_ #4446 [Book][Templating] refer to the VarDumper component for dump() (xabbuh) -- `ed5c61f `_ #4411 Added a reference to the Bootstrap 3 form theme (javiereguiluz) -- `766e01f `_ #4169 [Components][Form] document $deep and $flatten of getErrors() (xabbuh) -- `1d88a1b `_ #4443 Added the release dates for the upcoming Symfony 3 versions (javiereguiluz) -- `3329bd2 `_ #4424 [#4243] Tweaks to the new var-dumper component (weaverryan, nicolas-grekas) -- `9caea6f `_ #4336 [Form] Add entity manager instance support for em option (egeloen) -- `f2ab245 `_ #4374 [WCM] Revamped the Quick Start tutorial (javiereguiluz) -- `2c190ed `_ #4427 Update most important book articles to follow the best practices (WouterJ) -- `12a09ab `_ #4377 Added interlinking and fixed install template for reusable bundles (WouterJ) -- `8259d71 `_ #4425 Updating component usage to use composer require (weaverryan) -- `0e80aba `_ #4369 [reference][configuration][security]Added key_length for pbkdf2 encoder (Guillaume-Rossignol) -- `d1afa4d `_ #4243 [WIP] var-dumper component (nicolas-grekas) -- `5165419 `_ #4295 [Security] Hidden front controller for Nginx (phansys) -- `23f790a `_ #4058 Skip console commands from event listeners (tPl0ch) -- `4b98d48 `_ #3386 [Translation] added method to expose collected message (Grygir) -- `242d4f6 `_ #4319 Documentation for debug:event-dispatcher command (matthieuauger) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `9d599a0 `_ minor #4544 #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook (ternel) -- `6aabece `_ #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook -- `e96ebd3 `_ #4522 Add missing brackets to PropertyAccessor examples (loonytoons) -- `4f66d48 `_ #4506 SetDescription required on Product entities (yearofthegus) -- `85bf906 `_ #4444 fix elseif statement (MightyBranch) -- `ad14e78 `_ #4494 Updated the Symfony Installer installation instructions (javiereguiluz) -- `7cc4287 `_ #4442 replace doc role for bundle docs with external ref (xabbuh) -- `33bf462 `_ #4407 [Components][Console] array options need array default values (xabbuh) -- `2ab2e1f `_ #4342 Reworded a misleading Doctrine explanation (javiereguiluz) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `a109c4b `_ #4537 Update link to remove absolute URL (jms85, dangarzon) -- `05f5dba `_ #4536 Add Ryan Weaver as 10th core team member (ifdattic) -- `7b1ff2a `_ #4554 Changed url to PHP-CS-FIXER repository (jzawadzki) -- `9d599a0 `_ #4544 bug #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook (ternel) -- `7b3500c `_ #4542 Update conventions.rst (csuarez) -- `5aaba1e `_ #4529 Best Practices: Update link title to match cookbook article title (dangarzon) -- `ab8e7f5 `_ #4530 Book: Update link title to match cookbook article title (dangarzon) -- `bf61658 `_ #4523 Add missing semicolons to PropertyAccess examples (loonytoons) -- `8beadce `_ #4496 [Book][Security] link to a bundle's current (not master) docs (xabbuh) -- `43809b1 `_ #4479 remove versionadded directives for old versions (xabbuh) -- `5db8386 `_ #4462 [Reference] Fixed lots of things using the review bot (WouterJ) -- `dbfaac1 `_ #4459 Fix up the final sentence to be a bit cleaner. (micheal) -- `3761e50 `_ #4514 [Contributing][Documentation] typo fix (xabbuh) -- `21afb4c `_ #4445 Removed unnecessary use statement (Alex Salguero) -- `3969fd6 `_ #4432 [Reference][Twig] tweaks to the Twig reference (xabbuh) -- `188dd1f `_ #4400 Continues #4307 (SamanShafigh, WouterJ) -- `c008733 `_ #4399 Explain form() and form_widget() in form customization (oopsFrogs, WouterJ) -- `2139754 `_ #4253 Adder and remover sidenote (kshishkin) -- `b81eb4d `_ #4488 Terrible mistake! Comma instead of semicolon... (nuvolapl) -- `0ee3ae7 `_ #4481 [Cookbook][Cache] add syntax highlighting for Varnish code blocks (xabbuh) -- `0577559 `_ #4418 use the C lexer for Varnish config examples (xabbuh) -- `97d8f61 `_ #4403 Improved naming (WouterJ) -- `6298595 `_ #4453 Fixed make file (WouterJ) -- `0c7dd72 `_ #4475 Fixed typos (pborreli) -- `b847b2d `_ #4480 Fix spelling (nurikabe) -- `0d91cc5 `_ #4461 Update doctrine.rst (guiguiboy) -- `81fc1c6 `_ #4448 [Book][HTTP Cache] moved inlined URL to the bottom of the file (xabbuh) -- `6995b07 `_ #4435 consistent table headlines (xabbuh) -- `0380d34 `_ #4447 [Book] tweaks to #4427 (xabbuh) -- `eb0d8ac `_ #4441 Updated first code-block``::`` bash (Nitaco) -- `41bc061 `_ #4106 removed references to documentation from external sources (fabpot, WouterJ) -- `c9a8dff `_ #4352 [Best Practices] update best practices index (xabbuh) -- `8a93c95 `_ #4437 Correct link to scopes page (mayeco) -- `91eb652 `_ #4438 Fix typo: Objected => Object (ifdattic) -- `5d6d0c2 `_ #4436 remove semicolons in PHP templates (xabbuh) -- `97c4b2e `_ #4434 remove unused label (xabbuh) -- `4be6786 `_ #4326 [Components][Form] Grammar improvement (fabschurt) -- `a27238e `_ #4313 Improved and fixed twig reference (WouterJ) -- `1ce9dc5 `_ #4398 A few small improvements to the EventDispatcher Component docs (GeertDD) -- `42abc66 `_ #4421 [Best Practices] removed unused links in business-logic (77web) -- `61c0bc5 `_ #4419 [DependencyInjection] Add missing space in code (michaelperrin) - -October, 2014 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `d7ef1c7 `_ #4348 Updated information about handling validation of embedded forms to Valid... (peterrehm) -- `691b13d `_ #4340 [Cookbook][Web Server] add sidebar for the built-in server in VMs (xabbuh) -- `bd85865 `_ #4299 [Serializer] Handle circular references. symfony/symfony#12098. (dunglas) -- `d79c48d `_ #4280 [Cookbook][Cache] Added config example for Varnish 4.0 (thierrymarianne) -- `5849f7f `_ #4168 [Components][Form] describe how to access form errors (xabbuh) -- `c10e9c1 `_ #4371 Added a code example for emailing on 4xx and 5xx errors without 404's (weaverryan) -- `1117741 `_ #4159 [WCM][OptionsResolver] Adjusted the OptionsResolver documentation to describe the 2.6 API (webmozart, peterrehm) -- `0c57939 `_ #4327 First import of the "Official Best Practices" book (javiereguiluz) -- `2cd6646 `_ #4293 Document error page preview (Symfony ~2.6) (mpdude) -- `142c826 `_ #4005 [Cookbook][Web server] description for running PHP's built-in web server in the background (xabbuh) -- `8dc90ef `_ #4224 [Components][HttpKernel] outline implications of the kernel.terminate event (xabbuh) -- `d3b5ba2 `_ #4085 [Component][Forms] add missing features introduced in 2.3 (xabbuh) -- `f433e64 `_ #4099 Composer installation verbosity tip (dannykopping) -- `f583a45 `_ #4204 [Reference][Constraints] validate `````null````` (Expression constraint in 2.6) (xabbuh) -- `925a162 `_ #4290 Updating library/bundle install docs to use "require" (weaverryan) -- `86c67e8 `_ #4233 2.5 Validation API changes (nicolassing, lashae, Rootie, weaverryan) -- `0f34bb8 `_ #3956 [Command] Added LockHelper (lyrixx) -- `278de83 `_ #3930 [Console] Add Process Helper documentation (romainneutron) -- `44f570b `_ #4294 Improve cookbook entry for error pages in 2.3~ (mpdude) -- `3b6c2b9 `_ #4269 [Cookbook][External Parameters] Enhance content (bicpi) -- `25a17fe `_ #4264 [#4003] A few more form_themes config changes (weaverryan) -- `5b65654 `_ #3912 [Security] Added remote_user firewall info and documentation for pre authenticated firewalls (Maxime Douailin, mdouailin) -- `62bafad `_ #4246 [Reference] add description for the `````validation_groups````` option (xabbuh) -- `5d505bb `_ #4206 Added note about ProgressBar changes (kbond) -- `c2342a7 `_ #4241 [Form] Added information about float choice lists (peterrehm) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `dde6919 `_ #4390 Update custom_constraint.rst (luciantugui) -- `68a2c7b `_ #4381 Updated Valid constraint reference (inso) -- `dbb25b9 `_ #4379 [OptionsResolver] Fix wrong namespace in example (rybakit) -- `db01e57 `_ #4362 Missing apostrophe in source example. (astery) -- `d49d51f `_ #4350 Removed extra parenthesis (sivolobov) -- `e6d7d8f `_ #4315 Update choice.rst (odolbeau) -- `1b15d57 `_ #4300 [Components][PropertyAccess] Fix PropertyAccessorBuilder usage (Thierry Geindre) -- `061324f `_ #4297 [Cookbook][Doctrine] Fix typo in XML configuration for custom SQL functions (jdecool) -- `f81b7ad `_ #4292 Fixed broken external link to DemoController Test (danielsan) -- `9591a04 `_ #4284 change misleading language identifier (Kristof Van Cauwenbergh, kristofvc) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `217bf5f `_ #4353 [Cookbook][Controller] fix route prefix in PHP code example (xabbuh) -- `a4f7d51 `_ #4396 Corrected latin abbreviation (GeertDD) -- `ebf2927 `_ #4387 Inline condition removed for easier reading (acidjames) -- `aa70028 `_ #4375 Removed the redundant usage of layer. (micheal) -- `f3dd676 `_ #4394 update Sphinx extension submodule reference (xabbuh) -- `6406a27 `_ #4391 Removed unused use UsernameNotFoundException (boekkooi) -- `9e03f2d `_ #4388 Minor spelling fix (GeertDD) -- `4dfd607 `_ #4356 Remove incoherence between Doctrine and Propel introduction paragraphs (arnaugm) -- `1d71332 `_ #4344 [Templating] Added a sentence that explains what a Template Helper is (iltar) -- `22b9b27 `_ #4372 Tweaks after proofreading the 2.6 OptionsResolver stuff (weaverryan, WouterJ) -- `9a76309 `_ #4384 fix typo (kokoon) -- `eb752cc `_ #4363 Fixed sentence (WouterJ) -- `3e8aa59 `_ #4376 Cleaned up javascript code (flip111) -- `06e7c5f `_ #4364 changed submit button label (OskarStark) -- `d1810ca `_ #4357 fix Twig-extensions links (mhor) -- `e2e2915 `_ #4359 Added missing closing parenthesis to example. (mattjanssen) -- `f1bb8bb `_ #4358 Fixed link to documentation standards (sivolobov) -- `65c891d `_ #4355 Missing space (ErikSaunier) -- `7359cb4 `_ #4196 Clarified the bundle base template bit. (Veltar) -- `6ceb8cb `_ #4345 Correct capitalization for the Content-Type header (GeertDD) -- `3e4c92a `_ #4104 Use ${APACHE_LOG_DIR} instead of /var/log/apache2 (xamgreen) -- `3da0776 `_ #4338 ESI Variable Details Continuation (Farkie, weaverryan) -- `7f461d2 `_ #4325 [Components][Form] Correct a typo (fabschurt) -- `d162329 `_ #4276 [Components][HttpFoundation] Make a small grammatical adjustment (fabschurt) -- `69bfac1 `_ #4322 [Components][DependencyInjection] Correct a typo: replace "then" by "the" (fabschurt) -- `8073239 `_ #4318 [Cookbook][Bundles] Correct a typo: remove unnecessary "the" word (fabschurt) -- `228111b `_ #4316 Remove horizontal scrollbar (ifdattic) -- `34e22d6 `_ #4317 Remove horizontal scrollbar and change event name to follow conventions (ifdattic) -- `090afab `_ #4287 support Varnish in configuration blocks (xabbuh) -- `1603463 `_ #4306 Improve readability (ifdattic) -- `e5fed9d `_ #4303 Fix spelling (nurikabe) -- `31d7905 `_ #4302 View documentation had a reference to the wrong twig template (milan) -- `ef11ef4 `_ #4250 Clarifying Bundle Best Practices is for *reusable* bundles (weaverryan) -- `430eabf `_ #4298 Book HTTP Fundamentals routing example fixed with routing.xml file (peterkokot) -- `a535c9f `_ #4285 Update security.rst (placid2000) -- `7ab6df9 `_ #4237 Finished #3886 (ahsio, WouterJ) -- `990b453 `_ #4245 [Contributing] tweaks to the contribution chapter (xabbuh) - -September, 2014 ---------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `e8a1501 `_ #4201 [Components][Process] `````mustRun()````` documentation (xabbuh) -- `eac0e51 `_ #4195 Added a note about the total deprecation of YUI (javiereguiluz) -- `e44c791 `_ #4047 Documented info method (WouterJ) -- `2962e14 `_ #4003 [Twig][Form] Moved twig.form.resources to a higher level (stefanosala) -- `d5d46ec `_ #4017 Clarify that route defaults don't need a placeholder (iamdto) -- `1d56da4 `_ #4239 Remove redundant references to trusting HttpCache (thewilkybarkid) -- `c306b68 `_ #4249 provide node path on configuration (desarrolla2) -- `9f0f14e `_ #4210 Move debug commands to debug namespace (matthieuauger) -- `9b4b36f `_ #4236 Javiereguiluz bundle install instructions (WouterJ) -- `ea068c2 `_ #4202 [Reference][Constraints] caution on `````null````` values in Expression constraint (xabbuh) -- `a578de9 `_ #4223 Revamped the documentation about "Contributing Docs" (javiereguiluz) -- `de60dbe `_ #4182 Added note about exporting SYMFONY_ENV (jpb0104) -- `a8dc2bf `_ #4166 Translation custom loaders (raulfraile) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `c289ac8 `_ #4279 Double-quotes instead of single quotes (UnexpectedValueException in Windows 8) (galdiolo) -- `5500e0b `_ #4267 Fix error in bundle installation standard example (WouterJ) -- `082755d `_ #4240 [Components][EventDispatcher] fix ContainerAwareEventDispatcher definition (xabbuh) -- `2319d6a `_ #4213 Handle "constraints" option in form unit testing (sarcher) -- `c567707 `_ #4222 [Components][DependencyInjection] do not reference services in parameters (xabbuh) -- `02d1091 `_ #4209 Fix method for adding placholders in progressBar (danez) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `df16779 `_ #4226 add note about parameters in imports (xabbuh) -- `c332063 `_ #4278 Missing word in DependencyInjection => Types of Injection (fabschurt) -- `287c7bf `_ #4275 added Nicolas to the list of mergers for the new var dumper component (fabpot) -- `3a4e226 `_ #4263 Fixed typo (zebba) -- `187c255 `_ #4259 Added feature freeze dates for Symfony versions (javiereguiluz) -- `efc1436 `_ #4247 [Reference] link translation DIC tags to components section (xabbuh) -- `17addb1 `_ #4238 Finished #3924 (WouterJ) -- `19a0c35 `_ #4252 Removed unnecessary comma (allejo) -- `9fd91d6 `_ #4219 Cache needs be cleared (burki94) -- `025f02e `_ #4220 Added a note about the side effects of enabling both PHP and Twig (javiereguiluz) -- `46fcb67 `_ #4218 Caution that roles should start with ``ROLE_`` (jrjohnson) -- `78eea60 `_ #4077 Removed outdated translations from the official list (WouterJ) -- `2cf9e47 `_ #4171 Fixed version for composer install (zomberg) -- `5c62b36 `_ #4216 Update Collection.rst (azarzag) -- `8591b87 `_ #4215 Fixed code highlighting (WouterJ) -- `8f01195 `_ #4212 Missing backtick, thanks to @Baptouuuu (WouterJ) -- `f276e34 `_ #4205 replace "Symfony2" with "Symfony" (xabbuh) -- `6db13ac `_ #4208 Added a note about the lacking features of Yaml Component (javiereguiluz) -- `f8c6201 `_ #4200 Moved 'contributing' images to their own directory (javiereguiluz) -- `b4650fa `_ #4199 fix name of the Yaml component (xabbuh) -- `9d89bb0 `_ #4190 add link to form testing chapter in test section (xabbuh) - -August, 2014 ------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `bccb080 `_ #4140 [Cookbook][Logging] document multiple recipients in XML configs (xabbuh) -- `7a6e3d1 `_ #4150 Added the schema_filter option to the reference (peterrehm) -- `be90d8a `_ #4142 [Cookbook][Configuration] tweaks for the web server configuration chapter (xabbuh) -- `5379f54 `_ #4086 [Reference][Constraints] Added hint about attaching the expression constraint to a form field (peterrehm) -- `041105c `_ #3883 Removed redundant POST request exclusion info (ryancastle) -- `4f9fef6 `_ #4000 [Cookbook] add cookbook article for the server:run command (xabbuh) -- `4ea4dfe `_ #3915 [Cookbook][Configuration] documentation of Apache + PHP-FPM (xabbuh) -- `79cb4f1 `_ #4069 document the namespace alias (dbu) -- `08bed5f `_ #4128 Finished #3759 (WouterJ) -- `4d5adaa `_ #4125 Added link to JSFiddle example (WouterJ) -- `75bda4b `_ #4124 Rebased #3965 (WouterJ) -- `e2f13a4 `_ #4039 [DomCrawler] Added node name getter (fejese) -- `3f92d5f `_ #3966 [Cookbook][Controller] Add note about invokable controller services (kbond) -- `fdb8a32 `_ #3950 [Components][EventDispatcher] describe the usage of the RegisterListenersPass (xabbuh) -- `7e09383 `_ #3940 Updated docs for Monolog "swift" handler in cookbook. (phansys) -- `9d7c999 `_ #3895 [Validator] Support "maxSize" given in KiB (jeremy-derusse) -- `8adfe98 `_ #3894 Rewrote Extension & Configuration docs (WouterJ) -- `cafea43 `_ #3888 Updated the example used to explain page creation (javiereguiluz) -- `df0cf68 `_ #3885 [RFR] Added "How to Organize Configuration Files" cookbook (javiereguiluz) -- `41116da `_ #4081 [Components][ClassLoader] documentation for the ClassMapGenerator class (xabbuh) -- `2b9cb7c `_ #4076 Fixed description of session storage of the ApiKeyAuthenticator (peterrehm) -- `35a0f66 `_ #4102 Adding a new entry about reverse proxies in the framework (weaverryan) -- `95c2066 `_ #4096 labels in submit buttons + new screenshot (ricardclau) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `5fac303 `_ #4165 Update voters.rst (gerryvdm) -- `4882b99 `_ #4164 Fixed minor typos. (ahsio) -- `eaaa35a `_ #4145 Fix documentation for group_sequence_provider (giosh94mhz) -- `155c3e8 `_ #4153 [Reference] fix namespace in Expression constraint (xabbuh) -- `2c93aa5 `_ #4147 [Cookbook][Logging] add missing Monolog handler type in XML config (xabbuh) -- `53b2c2b `_ #4139 cleaned up the code example (gondo) -- `b5c9f2a `_ #4138 fixed wrongly linked dependency (gondo) -- `b486b22 `_ #4131 Replaced old way of specifying http method by the new one (Baptouuuu) -- `93481d7 `_ #4120 Fix use mistakes (mbutkereit) -- `c0a0120 `_ #4119 Fix class name in ConsoleTerminateListener example (alOneh) -- `4629d8b `_ #4116 Fixed the code snippets for the expression language functions (stof) -- `d699255 `_ #4083 [Reference] field dependent empty_data option description (xabbuh) -- `3ffc20f `_ #4103 [Cookbook][Forms] fix PHP template file name (xabbuh) -- `234fa36 `_ #4095 Fix php template (piotrantosik) -- `01fb9f2 `_ #4093 See #4091 (dannykopping) -- `8f3a261 `_ #4092 See #4091 (dannykopping) -- `7d39b03 `_ #4079 Fixed typo in filesystem component (kohkimakimoto) -- `f0bde03 `_ #4075 Fixed typo in the yml validation (timothymctim) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `e9d317a `_ #4160 [Reference] consistent & complete config examples (xabbuh) -- `3e68ee7 `_ #4152 Adding 'attr' option to the Textarea options list (ronanguilloux) -- `a7f3297 `_ #4136 [Reference] fix from suffix to prefix (xabbuh) -- `c4eb628 `_ #4130 A set of small typos (Baptouuuu) -- `236d8e0 `_ #4137 fixed directive syntax (WouterJ) -- `6e90520 `_ #4135 [#3940] Adding php example for an array of emails (weaverryan) -- `b37ee61 `_ #4132 Use proper way to reference a doc page for legacy sessions (Baptouuuu) -- `189a123 `_ #4129 [Components] consistent & complete config examples (xabbuh) -- `5ab5246 `_ #4127 Second part of #3848 (WouterJ) -- `46f3108 `_ #4126 Rebased #3848 (WouterJ) -- `84e6e7f `_ #4114 [Book] consistent and complete config examples (xabbuh) -- `03fcab1 `_ #4112 [Contributing][Documentation] add order of translation formats (xabbuh) -- `650120a `_ #4002 added Github teams for the core team (fabpot) -- `10792c3 `_ #3959 [book][cache][tip] added cache annotations. (aitboudad) -- `ebaed21 `_ #3944 Update dbal.rst (bpiepiora) -- `16e346a `_ #3890 [Components][HttpFoundation] use a placeholder for the constructor arguments (xabbuh) -- `7bb4f34 `_ #4115 [Documentation] [Minor] Changes foobar.net in example.com (magnetik) -- `12d0b82 `_ #4113 tweaks to the new reverse proxy/load balancer chapter (xabbuh) -- `4cce133 `_ #4057 Update introduction.rst (carltondickson) -- `26141d6 `_ #4080 [Reference] order form type options alphabetically (xabbuh) -- `7806aa7 `_ #4117 Added a note about the automatic handling of the memory spool in the CLI (stof) -- `5959b6c `_ #4101 [Contributing] extended Symfony 2.4 maintenance (xabbuh) -- `e2056ad `_ #4072 [Contributing][Code] add note on Symfony SE forks for bug reports (xabbuh) -- `b8687dd `_ #4091 Put version into quotes, otherwise it fails in ZSH (dannykopping) -- `665c091 `_ #4087 Typo (tvlooy) -- `f95bbf3 `_ #4023 [Cookbook][Security] usage of a non-default entity manager in an entity user provider (xabbuh) -- `27b1003 `_ #4074 Fixed (again) a typo: Toolbet --> Toolbelt (javiereguiluz) -- `c97418f `_ #4073 Reworded bundle requirement (WouterJ) -- `e5d5eb8 `_ #4066 Update inherit_data_option.rst (Oylex) -- `9c08572 `_ #4064 Fixed typo on tag service (saro0h) - -July, 2014 ----------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `1b4c1c8 `_ #4045 Added a new "Deploying to Heroku Cloud" cookbook article (javiereguiluz) -- `f943eee `_ #4009 Remove "Controllers extends ContainerAware" best practice (tgalopin) -- `eae9ad0 `_ #3875 Added a note about customizing a form with more than one template (javiereguiluz) -- `2ae4f34 `_ #3746 [Validator] Disallow empty file in FileValidator (megazoll) -- `1938c2f `_ #3724 Updated ISBN validator docs (sprain) -- `7c71b18 `_ #2952 Enabling profiler in test (danieledangeli) -- `d6787b7 `_ #3989 adde stof as a merger (fabpot) -- `4a9e49e `_ #3946 DQL custom functions on doctrine reference page (healdropper) -- `2b2d9d3 `_ #3972 Added PSR-4 to Class Loaders list (dosten) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `1b695b5 `_ #4063 fix parent form types (xabbuh) -- `7901005 `_ #4048 $this->request replaced by $request (danielsan) -- `f6123f1 `_ #4031 Update form_events.rst (redstar504) -- `99932cf `_ #4010 [Console] Fixed documentation for ProgressBar (VasekPurchart) -- `06f8c31 `_ #4012 Fix xml route configuration for routing condition (xavierbriand) -- `a2a628f `_ #4025 added CVE 2014-4931 (fabpot) -- `a1435e5 `_ #3998 [Console] Fixed QuestionHelper examples (florianv) -- `b32f9f2 `_ #3771 Fix function example in expression language component (raulfraile) -- `eb813a5 `_ #3979 removed invalid processors option (ricoli) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `a4bdb97 `_ #4070 Added a note about permissions in the Quick Tour (javiereguiluz) -- `a7fe00f `_ #4068 Remove diff info from cookbook/security/voters.rst (pmartelletti) -- `b3f15b2 `_ #4059 eraseCredentials method typo (danielsan) -- `44091b1 `_ #4053 Update doctrine.rst (sr972) -- `b06ad60 `_ #4052 [Security] [Custom Provider] Use properties on WebserviceUser (entering) -- `a834a7e `_ #4042 [Cookbook] apply headline guidelines to the cookbook articles (xabbuh) -- `f25faf3 `_ #4046 Fixed a syntax error (javiereguiluz) -- `3c660d1 `_ #4044 Added editorconfig (WouterJ) -- `ae3ec04 `_ #4041 [Cookbook][Deployment] link to the deployment index (xabbuh) -- `2e4fc7f `_ #4030 enclose YAML strings containing % with quotes (xabbuh) -- `9520d92 `_ #4038 Update rendered tag (kirill-oficerov) -- `f5c2602 `_ #4036 Update page_creation.rst (redstar504) -- `c2eda93 `_ #4034 Update internals.rst (redstar504) -- `a5ad0df `_ #4035 Update version in Rework your Patch section (yguedidi) -- `eed8d64 `_ #4026 Updating Symfony version from 2.4 to 2.5 (danielsan) -- `12752c1 `_ #4013 Removed wrong reference to cookbook (gquemener) -- `ec832dc `_ #3994 [Console] Fix Console component $app to $this and use of getHelper() method (eko) -- `d8b037a `_ #4019 Update twig_reference.rst (redstar504) -- `7ea87e6 `_ #4016 Fixed the format of one letter-based list (javiereguiluz) -- `579a873 `_ #4015 Fixed bad indenting (the list was treated as a blockquote) (javiereguiluz) -- `4669620 `_ #4004 use GitHub instead of Github (xabbuh) -- `a3fe74f `_ #3993 [Console] Fix Console component getHelperSet()->get() to getHelper() (eko) -- `a41af7e `_ #3880 document the mysterious abc part of the header (greg0ire) -- `90773b0 `_ #3990 Move the section about collect: false to the cookbook entry (weaverryan) -- `2ae8281 `_ #3864 plug rules for static methods (cordoval) -- `d882cc0 `_ #3988 fix typos. (yositani2002) -- `b67a059 `_ #3986 Rebased #3982 - Some fixes (WouterJ) -- `801c756 `_ #3977 [WCM] removed call to deprecated getRequest() method (Baptouuuu) -- `4c1d4ae `_ #3968 Proofreading the new Azure deployment article (weaverryan) - -June, 2014 ----------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `5540e0b `_ #3963 [cookbook] [deployment] added cookbook showing how to deploy to the Microsoft Azure Website Cloud (hhamon) -- `6cba0f1 `_ #3936 Varnish only takes into account max-age (gonzalovilaseca) -- `3c95af5 `_ #3928 Reorder page from simple to advanced (rebased) (clemens-tolboom) -- `350b805 `_ #3916 [Component][EventDispatcher] documentation for the TraceableEventDispatcher (xabbuh) -- `1702133 `_ #3913 [Cookbook][Security] Added doc for x509 pre authenticated listener (zefrog) -- `32b9058 `_ #3909 Update the CssSelector component documentation (stof) -- `23b51c8 `_ #3901 Bootstraped the standards for "Files and Directories" (javiereguiluz) -- `8931c36 `_ #3889 Fixed the section about getting services from a command (javiereguiluz) -- `9fddab6 `_ #3877 Added a note about configuring several paths under the same namespace (javiereguiluz) -- `eadf281 `_ #3874 Updated the installation instructions for Symfony 2.5+ (javiereguiluz) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `aeffd12 `_ #3961 Fixing php coding (mvhirsch) -- `84332ff `_ #3945 Fixed missing component name in namespaces (WouterJ) -- `d8329dc `_ #3943 Fixing simple quotes in double quotes (ptitlazy) -- `04f4318 `_ #3934 Move __construct after the repository assignment (cmodijk) -- `0626f2b `_ #3897 Collection constraint (hhamon) -- `3387cb2 `_ #3871 Fix missing Front Controller (parthasarathigk) -- `8257be9 `_ #3891 Fixed wrong method call. (cmfcmf) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `75ee6b4 `_ #3969 [cookbook] [deployment] removed marketing introduction in Azure Deployme... (hhamon) -- `02aeade `_ #3967 fix typo. (yositani2002) -- `208b0dc `_ #3951 fix origin of AcmeDemoBundle (hice3000) -- `fba083e `_ #3957 [Cookbook][Bundles] fix typos in the prepend extension chapter (xabbuh) -- `c444b5d `_ #3948 update the Sphinx extensions to raise warnings when backslashes are not ... (xabbuh) -- `8fef7b7 `_ #3938 [Contributing][Documentation] don't render the list inside a blockquote (xabbuh) -- `b7a03f8 `_ #3937 properly escape backslashes in class and method directives (xabbuh) -- `882471f `_ #3935 Typo (greg0ire) -- `222a014 `_ #3933 render directory inside a code block (xabbuh) -- `0c2a9b3 `_ #3931 [Component][EventDispatcher] 2.5 specific documentation for the TraceableEventDispatcher (xabbuh) -- `b31ea51 `_ #3929 Update custom_authentication_provider.rst (verschoof) -- `7937864 `_ #3927 [Cookbook][Security] Explicit 'your_user_provider' configuration parameter (zefrog) -- `26d00d0 `_ #3925 Fixed the indentation of two code blocks (javiereguiluz) -- `351b2cf `_ #3922 update fabpot Sphinx extensions version (xabbuh) -- `3ddbe1b `_ #3923 Fixed the headers of one table (javiereguiluz) -- `35cbffc `_ #3920 [Components][Form] remove blank line to render the versionadded directive properly (xabbuh) -- `df9f31a `_ #3882 change version numbers in installation notes to be in line with the docu... (xabbuh) -- `ed496ae `_ #3887 [Components][Form] add versionadded for the data collector form extension (xabbuh) -- `36337e7 `_ #3906 Blockquote introductions (xabbuh) -- `5e0e119 `_ #3899 [RFR] Misc. fixes mostly related to formatting issues (javiereguiluz) -- `349cbeb `_ #3900 Fixed the formatting of the table headers (javiereguiluz) -- `1dc8b4a `_ #3898 clarifying the need of a factory for auth-provider (leberknecht) -- `0c20141 `_ #3896 Fixing comment typo for Doctrine findBy and findOneBy code example (beenanner) -- `b00573c `_ #3870 Fix wrong indentation for lists (WouterJ) - -May, 2014 ---------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `4fd1b49 `_ #3753 [DependencyInjection] Add documentation about service decoration (romainneutron) -- `f913dd7 `_ #3603 [Serializer] Support for is.* getters in GetSetMethodNormalizer (tiraeth) -- `e8511cb `_ #3776 Updated event_listener.rst (bfgasparin) -- `af8c20f `_ #3818 [Form customization] added block_name example. (aitboudad) -- `c788325 `_ #3841 [Cookbook][Logging] register processor per handler and per channel (xabbuh) -- `979533a `_ #3839 document how to test actions (greg0ire) -- `d8aaac3 `_ #3835 Updated framework.ide configuration (WouterJ) -- `a9648e8 `_ #3742 [2.5][Templating] Add documentation about generating versioned URLs (romainneutron) -- `f665e14 `_ #3704 [Form] Added documentation for Form Events (csarrazi) -- `14b9f14 `_ #3777 added docs for the core team (fabpot) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `0649c21 `_ #3869 Add a missing argument to the PdoSessionHandler (jakzal) -- `259a2b7 `_ #3866 [Book][Security]fixed Login when there is no session. (aitboudad) -- `9b7584f `_ #3863 Error in XML (tvlooy) -- `0cb9c3b `_ #3827 Update 'How to Create and store a Symfony2 Project in Git' (nicwortel) -- `4ed9a08 `_ #3830 Generate an APC prefix based on __FILE__ (trsteel88) -- `9a65412 `_ #3840 Update dialoghelper.rst (jdecoster) -- `1853fea `_ #3716 Fix issue #3712 (umpirsky) -- `baa9759 `_ #3791 Property access tweaks (weaverryan) -- `80d70a4 `_ #3779 [Book][Security] constants are defined in the SecurityContextInterface (xabbuh) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `302fa82 `_ #3872 Update hostname_pattern.rst (sofany) -- `50672f7 `_ #3867 fixed missing info about FosUserBundle. (aitboudad) -- `3e3004f `_ #3865 Fixed link. (aitboudad) -- `b32ec15 `_ #3856 Update voters_data_permission.rst (MarcomTeam) -- `bffe163 `_ #3859 Add filter cssrewrite (DOEO) -- `f617ff8 `_ #3764 Update testing.rst (NAYZO) -- `3792fee `_ #3858 Clarified Password Encoders example (WouterJ) -- `663d68c `_ #3857 Added little bit information about the route name (WouterJ) -- `797cbd5 `_ #3794 Adds link to new QuestionHelper (weaverryan) -- `4211bff `_ #3852 Fixed link and typo in type_guesser.rst (rpg600) -- `78ae7ec `_ #3845 added link to /cookbook/security/force_https. (aitboudad) -- `6c69362 `_ #3846 [Routing][Loader] added JMSI18nRoutingBundle (aitboudad) -- `136864b `_ #3844 [Components] Fixed some typos. (ahsio) -- `b0710bc `_ #3842 Update dialoghelper.rst (bijsterdee) -- `9f1a354 `_ #3804 [Components][DependencyInjection] add note about a use case that requires to compile the container (xabbuh) -- `d92c522 `_ #3769 Updated references to new Session() (scottwarren) -- `00f60a8 `_ #3837 More asset version details (weaverryan) -- `681ddc8 `_ #3843 [Changelog] fix literal positions (xabbuh) -- `1aa79d5 `_ #3834 fix the wording in versionadded directives (for the master branch) (xabbuh) -- `7288a33 `_ #3789 [Reference][Forms] Improvements to the form type (xabbuh) -- `72fae25 `_ #3790 [Reference][Forms] move versionadded directives for form options directly below the option's headline (xabbuh) -- `b4d4ac3 `_ #3838 fix filename typo in cookbook/form/unit_testing.rst (hice3000) -- `0b06287 `_ #3836 remove unnecessary rewrite from nginx conf (Burgov) -- `89d0dae `_ #3833 fix the wording in versionadded directives (for the 2.4 branch) (xabbuh) -- `e58e39f `_ #3832 fix the wording in versionadded directives (for the 2.3 branch) (xabbuh) -- `09d6ca1 `_ #3829 [Components] consistent headlines (xabbuh) -- `54e0882 `_ #3828 [Contributing] consistent headlines (xabbuh) -- `b1336d7 `_ #3823 Added empty line after if statements (zomberg) -- `79b9fdc `_ #3822 Update voters_data_permission.rst (mimol91) -- `69cb7b8 `_ #3821 Update custom_authentication_provider.rst (leberknecht) -- `9f602c4 `_ #3820 Update page_creation.rst (adreeun) -- `52518c0 `_ #3819 Update csrf_in_login_form.rst (micheal) -- `1adfd9b `_ #3802 Add a note about which types can be used in Symfony (fabpot) -- `fa27ded `_ #3801 [Cookbook][Form] Fixed Typo & missing word. (ahsio) -- `127beed `_ #3770 Update factories.rst (AlaaAttya) -- `822d985 `_ #3817 Update translation.rst (richardpi) -- `241d923 `_ #3813 [Reference][Forms]fix time field count. (yositani2002) -- `bc96f55 `_ #3812 [Cookbook][Configuration] Fixed broken link. (ahsio) -- `5867327 `_ #3809 Fixed typo (WouterJ) -- `678224e `_ #3808 Fixed broken link in "Handling Authentication Failure" (stacyhorton) - -April, 2014 ------------ - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `322972e `_ #3803 [Book][Validation] configuration examples for the GroupSequenceProvider (xabbuh) -- `9e129bc `_ #3752 [Console] Add documentation for QuestionHelper (romainneutron) -- `64a924d `_ #3756 [WCM][Console] Add Process Helper documentation (romainneutron) -- `d4ca16a `_ #3743 Improve examples in parent services (WouterJ) -- `be4b9d3 `_ #3729 Added documentation for the new ``PropertyAccessor::isReadable()`` and ``isWritable()`` methods (webmozart) -- `70a3893 `_ #3774 [Book][Internals] add description for the kernel.finish_request event (xabbuh) -- `1934720 `_ #3461 [Form] Deprecated max_length and pattern options (stefanosala) -- `d611e77 `_ #3701 [Serializer] add documentation for serializer callbacks (cordoval) -- `80c645c `_ #3719 Fixed event listeners priority (tony-co) -- `c062d81 `_ #3469 [Validator] - EmailConstraint reference (egulias) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `f801e2e `_ #3805 Add missing autocomplete argument in askAndValidate method (ifdattic) -- `a81d367 `_ #3786 replaceArguments should be setArguments (RobinvdVleuten) -- `33b64e1 `_ #3788 Fix link for StopwatchEvent class (rpg600) -- `2ebabfb `_ #3792 Update commands_as_services.rst (mimol91) -- `529d4ce `_ #3761 buildViewBottomUp has been renamed to finishView (Nyholm) -- `d743139 `_ #3768 the Locale component does not have elements tagged with @api (xabbuh) -- `2b8e44d `_ #3747 Fix Image constraint class and validator link (weaverryan) -- `fa362ca `_ #3741 correct RuntimeException reference (shieldo) -- `d92545e `_ #3734 [book] [testing] fixed the path of the phpunit.xml file (javiereguiluz) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `136f98c `_ #3784 [Expression Langage] be consistent in "print/print out" uses (mickaelandrieu) -- `1094a13 `_ #3807 Added some exceptions to the method order in CS (stof) -- `55442b5 `_ #3800 Fixed another blockquote rendering issue (WouterJ) -- `969fd71 `_ #3785 ensure that destination directories don't exist before creating them (xabbuh) -- `79322ff `_ #3799 Fix list to not render in a block quote (WouterJ) -- `1a6f730 `_ #3793 language tweak for the tip introduced in #3743 (xabbuh) -- `dda9e88 `_ #3778 Adding information on internal reverse proxy (tcz) -- `d36bbd9 `_ #3765 [WIP] make headlines consistent with our standards (xabbuh) -- `daa81a0 `_ #3766 [Book] add note about services and the service container in the form cha... (xabbuh) -- `4529858 `_ #3767 [Book] link to the bc promise in the stable API description (xabbuh) -- `a5471b3 `_ #3775 Fixed variable naming (peterrehm) -- `703c2a6 `_ #3772 [Cookbook][Sessions] some language improvements (xabbuh) -- `3d30b56 `_ #3773 modify Symfony CMF configuration values in the build process so that the... (xabbuh) -- `cfd6d7c `_ #3758 [Book][Routing] Fixed typo on PHP version of a route definition (saro0h) -- `cedfdce `_ #3757 Fixed a typo in the request formats configuration page (gquemener) -- `6bd134c `_ #3754 ignore more files and directories which are created when building the documentation (xabbuh) -- `610462e `_ #3755 [Cookbook][Security] Firewall resitrction tweaks, fix markup, add to toc (xabbuh) -- `0a21718 `_ #3695 Firewall backport (weaverryan) -- `54d6a9e `_ #3736 [book] Misc. routing fixes (javiereguiluz) -- `f149dcf `_ #3739 [book] [forms] misc. fixes and tweaks (javiereguiluz) -- `ce582ec `_ #3735 [book] [controller] fixed the code of a session sample code (javiereguiluz) -- `499ba5c `_ #3733 [book] [validation] fixed typos (javiereguiluz) -- `4d0ff8f `_ #3732 Update routing.rst. Explain using url() v. path(). (ackerman) -- `44c6273 `_ #3727 Added a note about inlined private services (javiereguiluz) - -March, 2014 ------------ - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `3b640aa `_ #3644 made some small addition about our BC promise and semantic versioning (fabpot) -- `2d1ecd9 `_ #3525 Update file_uploads.rst (juanmf) -- `b1e8f56 `_ #3368 The host parameter has to be in defaults, not requirements (MarieMinasyan) -- `b34fb64 `_ #3619 [Validator] Uuid constraint reference (colinodell) -- `d7027c0 `_ #3418 [Validation] Add "hasser" support (bicpi) -- `4fd5fc1 `_ #3539 [Stopwatch] Describe retrieval of StopwatchEvent (jochenvdv) -- `1908a15 `_ #3696 [Console] Added standalone PSR-3 compliant logger (dunglas) -- `c75b1a7 `_ #3621 [Console] Command as service (gnugat) -- `00a462a `_ minor #3658 Fix PSR coding standards error (ifdattic) -- `acf255d `_ #3328 [WIP] Travis integration (WouterJ) -- `450146e `_ #3681 Enhanced Firewall Restrictions docs (danez) -- `3e7028d `_ #3659 [Internals] Complete notification description for kernel.terminate (bicpi) -- `db3cde7 `_ #3124 Add note about the property attribute (Property Accessor) (raziel057) -- `5965ec8 `_ #3420 [Cookbook][Configuration] add configuration cookbook handlig parameters in Configurator class (cordoval) -- `dcf8e6e `_ #3402 Added documentation about new requests formats configuration (gquemener) -- `a1050eb `_ #3411 [Cookbook][Dynamic Form Modification] Add AJAX sample (bicpi) -- `842fd30 `_ #3683 [TwigBundle] Add documentation about generating absolute URL with the asset function (romainneutron) -- `fc1576a `_ #3664 [Process] Add doc for ``Process::disableOutput`` and ``Process::enableOutput`` (romainneutron) -- `3731e2e `_ #3686 Documentation of the new PSR-4 class loader. (derrabus) -- `5b915c2 `_ #3629 Added documentation for translation:debug (florianv) -- `6951460 `_ #3601 Added documentation for missing ctype extension (slavafomin) -- `df63740 `_ #3627 added docs for the new Table console helper (fabpot) -- `96bd81b `_ #3626 added documentation for the new Symfony 2.5 progress bar (fabpot) -- `b02c16a `_ #3565 added information on AuthenticationFailureHandlerInterface (samsamm777) -- `2657ee7 `_ #3597 Document how to create a custom type guesser (WouterJ) -- `5ad1599 `_ #3577 Development of custom error pages is impractical if you need to set kernel.debug=false (mpdude) -- `3f4b319 `_ #3610 [HttpFoundation] Add doc for ``Request::getContent()`` method (bicpi) -- `56bc266 `_ #3589 Finishing the Templating component docs (WouterJ) -- `d881181 `_ #3588 Documented all form variables (WouterJ) -- `5cda1c7 `_ #3311 Use KernelTestCase instead of WebTestCase for testing code only requiring the Container (johnkary) -- `e96e12d `_ #3234 [Cookbook] New cookbok: How to use the Cloud to send Emails (bicpi) -- `d5d64ce `_ #3436 [Reference][Form Types] Add missing docs for "action" and "method" option (bicpi) -- `3df34af `_ #3490 Tweaking Doctrine book chapter (WouterJ) -- `b9608a7 `_ #3594 New Data Voter Article (continuation) (weaverryan) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `cad38ae `_ #3721 tweaks to the Console logger (xabbuh) -- `06c56c1 `_ #3709 [Components][Security] Fix #3708 (bicpi) -- `aadc61d `_ #3707 make method supportsClass() in custom voter compatible with the interface's documentation (xabbuh) -- `65150f9 `_ #3637 Update render_without_controller.rst (94noni) -- `9fcccc7 `_ #3634 Fix goal of “framework.profiler.only_exceptions“ option which profile on each exceptions on controller (not only 500) (stephpy) -- `9dd8d96 `_ #3689 Fix cache warmer description (WouterJ) -- `6221f35 `_ #3671 miss extends keyword in define BlogController class (ghanbari) -- `4ce7a15 `_ #3543 Fix the definition of customizing form's global errors. (mtrojanowski) -- `5d4a3a4 `_ #3343 [Testing] Fix phpunit test dir paths (bicpi) -- `badaae7 `_ #3622 [Components][Routing] Fix addPrefix() sample code (bicpi) -- `de0a5e1 `_ #3665 [Cookbook][Test] fix sample code (inalgnu) -- `4ef746a `_ #3614 [Internals] Fix Profiler:find() arguments (bicpi) -- `0c41762 `_ #3600 [Security][Authentication] Fix instructions for creating password encoders (bicpi) -- `0ab1f24 `_ #3593 Clarified Default and ClassName groups (WouterJ) -- `178984b `_ #3648 [Routing] Remove outdated tip about sticky locale (bicpi) -- `fc28453 `_ #3039 use DebugClassLoader class from Decomponent instead of the one from ... (xabbuh) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `abca098 `_ #3726 Minor tweaks after merging #3644 by @stof and @xabbuh (weaverryan) -- `d16be31 `_ #3725 Minor tweaks related to #3368 (weaverryan) -- `aa9bb25 `_ #3636 Update security.rst (nomack84) -- `78425c6 `_ #3722 add "Commands as Services" chapter to the cookbook's map (xabbuh) -- `9f26da8 `_ #3720 [#3539] A backport of a sentence - the parts that apply to 2.3 (weaverryan) -- `4b611d6 `_ #3717 [master] Fixed versionadded blocks (WouterJ) -- `5a3ba1b `_ #3715 change variable name to a better fitting one (xabbuh) -- `499eb6c `_ #3714 [2.4] Versionadded consistency (WouterJ) -- `e7580c0 `_ #3713 Updated versionadded directives to use "introduced" (WouterJ) -- `e15afe0 `_ #3711 Simplified the Travis configuration (stof) -- `db1cda5 `_ #3700 [Cookbook][Security] Firewall restrictions tweaks (xabbuh) -- `5035837 `_ #3706 Add support for nginx (guiditoito) -- `00a462a `_ #3658 Fix PSR coding standards error (ifdattic) -- `868de1e `_ #3698 Dynamic form modification cookbook: Fix inclusion of code (michaelperrin) -- `15a9d25 `_ #3697 [Console] Change Command namespaces (dunglas) -- `41b2eb8 `_ #3693 Tweak to Absolute URL generation (weaverryan) -- `bd473db `_ #3563 Add another tip to setup permissions (tony-co) -- `67129b1 `_ #3611 [Reference][Forms] add an introductory table containing all options of the basic form type (xabbuh) -- `fd8f7ae `_ #3694 fix the referenced documents names (xabbuh) -- `d617011 `_ #3657 Fix typos, remove trailing whitespace. (ifdattic) -- `1b4f6a6 `_ #3656 Minimize horizontal scrolling, add missing characters, remove trailing whitespace. (ifdattic) -- `7c0c5d1 `_ #3653 Http cache validation rewording (weaverryan) -- `0fb2c5f `_ #3651 [Reference][Forms] remove the label_attr option which is not available in the button type (xabbuh) -- `69ac21b `_ #3642 Fixed some typos and formatting issues (javiereguiluz) -- `93c35d0 `_ #3641 Added some examples to the "services as parameters" section (javiereguiluz) -- `12a6676 `_ #3640 [minor] fixed one typo and one formatting issue (javiereguiluz) -- `9967b0c `_ #3638 [#3116] Fixing wrong table name - singular is used elsewhere (weaverryan) -- `4fbf1cd `_ #3635 [QuickTour] close opened literals (xabbuh) -- `27b3410 `_ #3692 [Book][Translations] fixing a code block (xabbuh) -- `2192c32 `_ #3650 Fixing some build errors (xabbuh) -- `fa3f531 `_ #3677 [Reference][Forms] Remove variables section from tables (xabbuh) -- `cd6d1de `_ #3676 remove unnecessary code block directive (xabbuh) -- `07822b8 `_ #3675 add missing code block directive (xabbuh) -- `739f43f `_ #3669 Fixed syntax highlighting (rvanlaarhoven) -- `1f384bc `_ #3631 Added documentation for message option of the ``True`` constraint (naitsirch) -- `f6a41b9 `_ #3630 Minor tweaks to form action/method (weaverryan) -- `ae755e0 `_ #3628 Added anchor for permissions (WouterJ) -- `6380113 `_ #3667 Update index.rst (NAYZO) -- `97ef2f7 `_ #3566 Changes ACL permission setting hints (MicheleOnGit) -- `9f7d742 `_ #3654 [Cookbook][Security] Fix VoterInterface signature (bicpi) -- `0a65b6f `_ #3608 [Reference][Forms] add versionadded directive for multiple option of file type (xabbuh) -- `e34204e `_ #3605 Fixed a plural issue (benjaminpaap) -- `e7d5a45 `_ #3599 [CHANGELOG] fix reference to contributing docs (xabbuh) -- `3582bf1 `_ #3598 add changelog to hidden toctree (xabbuh) -- `58b7f96 `_ #3596 [HTTP Cache] Validation model: Fix header name (bicpi) -- `6d1378e `_ #3592 Added a tip about hardcoding URLs in functional tests (javiereguiluz) -- `04cf9f8 `_ #3595 Collection of fixes and improvements (bicpi) -- `2ed0943 `_ #3645 Adjusted the BC rules to be consistent (stof) -- `664a0be `_ #3633 Added missing PHP syntax coloration (DerekRoth) -- `1714a31 `_ #3585 Use consistent method chaining in BlogBundle sample application (ockcyp) -- `cb61f4f `_ #3581 Add missing hyphen in HTTP Fundamentals page (ockcyp) - -February, 2014 --------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `9dcf467 `_ #3613 Javiereguiluz revamped quick tour (weaverryan) -- `89c6f1d `_ #3439 [Review] Added detailed Backwards Compatibility Promise text (webmozart) -- `0029408 `_ #3558 Created Documentation CHANGELOG (WouterJ) -- `f6dd678 `_ #3548 Update forms.rst (atmosf3ar) -- `9676f2c `_ #3523 [Components][EventDispatcher] describe that the event name and the event dispatcher are passed to even... (xabbuh) -- `5c367b4 `_ #3517 Fixed OptionsResolver component docs (WouterJ) -- `527c8b6 `_ #3496 Added a section about using named assets (vmattila) -- `8ccfe85 `_ #3491 Added doc for named encoders (tamirvs) -- `46377b2 `_ #3486 Documenting createAccessDeniedException() method (klaussilveira) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `5c4336a `_ #3570 Callback: [Validator, validate] expects validate to be static (nixilla) -- `5c367b4 `_ #3517 Fixed OptionsResolver component docs (WouterJ) -- `adcbb5d `_ #3615 Fixes to cookbook/doctrine/registration_form.rst (Crushnaut) -- `a21fb26 `_ #3559 Remove reference to copying parameters.yml from Git cookbook (pwaring) -- `de71a51 `_ #3551 [Cookbook][Dynamic Form Modification] Fix sample code (rybakit) -- `143db2f `_ #3550 Update introduction.rst (taavit) -- `384538b `_ #3549 Fixed createPropertyAccessorBuilder usage (antonbabenko) -- `642e776 `_ #3544 Fix build errors (xabbuh) -- `d275302 `_ #3541 Update generic_event.rst (Lumbendil) -- `819949c `_ #3537 Add missing variable assignment (colinodell) -- `d7e8262 `_ #3535 fix form type name. (yositani2002) -- `821af3b `_ #3493 Type fix in remove.rst (weaverryan) -- `003230f `_ #3530 Update form_customization.rst (dczech) -- `a43f15a `_ #3519 [Book][Service Container] Fix syntax highlighting (iamdto) -- `86e02c6 `_ #3514 Fixed some small typos in code example (RobinvdVleuten) -- `696313c `_ #3513 [Component-DI] Fixed typo (saro0h) -- `27dcebd `_ #3509 Fix typo: side.bar.twig => sidebar.twig (ifdattic) -- `0dc8c26 `_ #3507 Fix a typo (missing `````) in ``:doc:`` link (ifdattic) -- `272197b `_ #3504 fix include directive so that the contents are really included (xabbuh) -- `e385d28 `_ #3503 file extension correction xfliff to xliff (nixilla) -- `6d34aa6 `_ #3478 Update custom_password_authenticator.rst (piotras-s) -- `a171700 `_ #3477 Api key user provider should use "implements" instead of "extends" (skowi) -- `7fe0de3 `_ #3475 Fixed doc for framework.session.cookie_lifetime reference. (tyomo4ka) -- `8155e4c `_ #3473 Update proxy_examples.rst (AZielinski) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `0928249 `_ #3568 Update checkbox_compound.rst.inc (joshuaadickerson) -- `38def3b `_ #3567 Update checkbox_compound.rst.inc (joshuaadickerson) -- `15d8ab8 `_ #3553 Minimize horizontal scrolling in code blocks to improve readability (ifdattic) -- `5120863 `_ #3547 Update acl.rst (iqfoundry) -- `b7ac326 `_ #3557 Minimize horizontal scrolling in code block to improve readability (ifdattic) -- `d974c77 `_ #3556 Fix PSR error (ifdattic) -- `f4bb017 `_ #3555 Wrap variables in {} for safer interpolation (ifdattic) -- `5f02bca `_ #3552 Fix typos (ifdattic) -- `6e32c47 `_ #3546 Fix README: contributions should be based off 2.3 or higher (colinodell) -- `ffa8f76 `_ #3545 Example of getting entity managers directly from the container (colinodell) -- `6a2a55b `_ #3579 Fix build errors (xabbuh) -- `dce2e23 `_ #3532 Added tip for Entity Listeners (slavafomin) -- `73adf8b `_ #3528 Clarify service parameters usages (WouterJ) -- `7e75b64 `_ #3533 Moving the new named algorithms into their own cookbook entry (weaverryan) -- `f634600 `_ #3531 Remove horizontal scrolling in code block (ifdattic) -- `9ba4fa7 `_ #3527 Changes to components domcrawler (ifdattic) -- `8973c81 `_ #3526 Changes for Console component (ifdattic) -- `6848bed `_ #3538 Rebasing #3518 (weaverryan) -- `c838df8 `_ #3511 [Component-DI] Removed useless else statement in code example (saro0h) -- `1af6742 `_ #3510 add empty line (lazyants) -- `1131247 `_ #3508 Add 'in XML' for additional clarity (ifdattic) -- `a650b93 `_ #3506 Nykopol overriden options (weaverryan) -- `ab10035 `_ #3505 replace Akamaï with Akamai (xabbuh) -- `7f56c20 `_ #3501 [Security] Fix markup (tyx) -- `80a90ba `_ #3500 Minimize horizontal scrolling in code blocks (improve readability) (ifdattic) -- `e5bc4ea `_ #3498 Remove second empty data (xabbuh) -- `d084d87 `_ #3485 [Cookbook][Assetic] Fix "javascripts" tag name typo (bicpi) -- `3250aba `_ #3481 Fix code block (minimise horizontal scrolling), typo in yaml (ifdattic) - -January, 2014 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `d52f3f8 `_ #3454 [Security] Add host option (ghostika) -- `11e079b `_ #3446 [WCM] Documented deprecation of the apache router. (jakzal) -- `0a0bf4c `_ #3437 Add info about callback in options resolver (marekkalnik) -- `6db5f23 `_ #3426 New Feature: Change the Default Command in the Console component (danielcsgomes) -- `6b3c424 `_ #3428 Translation - Added info about JsonFileLoader added in 2.4 (singles) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `fb22fa0 `_ #3456 remove duplicate label (xabbuh) -- `a87fe18 `_ #3470 Fixed typo (danielcsgomes) -- `c205bc6 `_ #3468 enclose YAML string with double quotes to fix syntax highlighting (xabbuh) -- `89963cc `_ #3463 Fix typos in cookbook/testing/database (ifdattic) -- `e0a52ec `_ #3460 remove confusing outdated note on interactive rebasing (xabbuh) -- `6831b13 `_ #3455 [Contributing][Code] fix indentation so that the text is rendered properly (xabbuh) -- `ea5816f `_ #3433 [WIP][Reference][Form Types] Update "radio" form type (bicpi) -- `42c80d1 `_ #3448 Overridden tweak (weaverryan) -- `bede4c3 `_ #3447 Fix error in namespace when use TokenInterface (joanteixi) -- `d9d7c58 `_ #3444 Fix issue #3442 (ifdattic) -- `a6ad607 `_ #3441 [Expression]Change title 'Accessing Public Methods' (Pyrech) -- `9e2e64b `_ #3427 Removed code references to Symfony Standard Distribution (danielcsgomes) -- `3c2c5fc `_ #3435 Update custom_password_authenticator.rst (boardyuk) -- `26b8146 `_ #3415 [#3334] the data_class option was not introduced in 2.4 (xabbuh) -- `0b2a491 `_ #3414 add missing code-block directive (xabbuh) -- `4988118 `_ #3432 [Reference][Form Types] Add "max_length" option in form type (nykopol) -- `26a7b1b `_ #3423 [Session Configuration] add clarifying notes on session save handler proxies (cordoval) -- `f2f5e9a `_ #3421 [Contributing] Cleaning the "contributing patch" page a bit (lemoinem) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `f285d93 `_ #3451 some language tweaks (AE, third-person perspective) (xabbuh) -- `b9bbe5d `_ #3499 Fix YAML syntax highlight + remove trailing whitespace (ifdattic) -- `2b7e0f6 `_ #3497 Fix highlighting (WouterJ) -- `2746067 `_ #3472 Fixed `````versionadded````` inconsistencies in Symfony 2.5+ (danielcsgomes) -- `a535ae0 `_ #3471 Fixed `````versionadded````` inconsistencies in Symfony 2.3 (danielcsgomes) -- `f077a8e `_ #3465 change wording in versionadded example to be consistent with what we use... (xabbuh) -- `f9f7548 `_ #3462 Replace ... with etc (ifdattic) -- `65efcc4 `_ #3445 [Reference][Form Types] Add missing (but existing) options to "form" type (bicpi) -- `1d1b91d `_ #3431 [Config] add cautionary note on ini file loader limitation (cordoval) -- `f2eaf9b `_ #3419 doctrine file upload example uses dir -- caution added (cordoval) -- `72b53ad `_ #3404 [#3276] Trying to further clarify the session storage directory details (weaverryan) -- `67b7bbd `_ #3413 [Cookbook][Bundles] improve explanation of code block for bundle removal (cordoval) -- `7c5a914 `_ #3369 Indicate that Group Sequence Providers can use YAML (karptonite) -- `1e0311e `_ #3416 add empty_data option where required option is used (xabbuh) -- `2be3f52 `_ #3422 [Cookbook][Custom Authentication Provider] add a note of warning for when forbidding anonymous users (cordoval) -- `e255de9 `_ #3429 [Reference][Form Types] Document "with_minutes" time/datetime option (bicpi) diff --git a/components/browser_kit.rst b/components/browser_kit.rst index bbfdd274034..23f3daa8c99 100644 --- a/components/browser_kit.rst +++ b/components/browser_kit.rst @@ -75,6 +75,19 @@ The value returned by the ``request()`` method is an instance of the :doc:`DomCrawler component `, which allows accessing and traversing HTML elements programmatically. +The :method:`Symfony\\Component\\BrowserKit\\Client::xmlHttpRequest` method, +which defines the same arguments as the ``request()`` method, is a shortcut to +make AJAX requests:: + + use Acme\Client; + + $client = new Client(); + // the required HTTP_X_REQUESTED_WITH header is added automatically + $crawler = $client->xmlHttpRequest('GET', '/'); + +.. versionadded:: 4.1 + The ``xmlHttpRequest()`` method was introduced in Symfony 4.1. + Clicking Links ~~~~~~~~~~~~~~ diff --git a/components/cache.rst b/components/cache.rst index d728f4455e0..23b6dca6606 100644 --- a/components/cache.rst +++ b/components/cache.rst @@ -13,9 +13,6 @@ The Cache Component It is designed to have a low overhead and it ships with ready to use adapters for the most common caching backends. -.. versionadded:: 3.3 - The PSR-16 "Simple Cache" implementation was introduced in Symfony 3.3. - Installation ------------ diff --git a/components/cache/adapters/memcached_adapter.rst b/components/cache/adapters/memcached_adapter.rst index 65bd5360cf3..57d3fc9abd4 100644 --- a/components/cache/adapters/memcached_adapter.rst +++ b/components/cache/adapters/memcached_adapter.rst @@ -7,10 +7,6 @@ Memcached Cache Adapter ======================= -.. versionadded:: 3.3 - - The Memcached adapter was introduced in Symfony 3.3. - This adapter stores the values in-memory using one (or more) `Memcached server`_ instances. Unlike the :ref:`APCu adapter `, and similarly to the :ref:`Redis adapter `, it is not limited to the current server's @@ -68,10 +64,6 @@ helper method allows creating and configuring a `Memcached`_ class instance usin // etc... )); -.. versionadded:: 3.4 - The feature to pass configuration options in the memcached DSN was - introduced in Symfony 3.4. - The `Data Source Name (DSN)`_ for this adapter must use the following format: .. code-block:: text diff --git a/components/cache/adapters/pdo_doctrine_dbal_adapter.rst b/components/cache/adapters/pdo_doctrine_dbal_adapter.rst index 3a2f5b9425c..e80e0738ab2 100644 --- a/components/cache/adapters/pdo_doctrine_dbal_adapter.rst +++ b/components/cache/adapters/pdo_doctrine_dbal_adapter.rst @@ -7,10 +7,6 @@ PDO & Doctrine DBAL Cache Adapter ================================= -.. versionadded:: 3.2 - - The PDO & Doctrine DBAL adapter was introduced in Symfony 3.2. - This adapter stores the cache items in an SQL database. It requires a `PDO`_, `Doctrine DBAL Connection`_, or `Data Source Name (DSN)`_ as its first parameter, and optionally a namespace, default cache lifetime, and options array as its second, diff --git a/components/cache/cache_invalidation.rst b/components/cache/cache_invalidation.rst index eb5bdf6cfa1..f1ba9eca226 100644 --- a/components/cache/cache_invalidation.rst +++ b/components/cache/cache_invalidation.rst @@ -20,9 +20,6 @@ The Symfony Cache component provides two mechanisms to help solving this problem Using Cache Tags ---------------- -.. versionadded:: 3.2 - Tags based invalidation was introduced in Symfony 3.2. - To benefit from tags based invalidation, you need to attach the proper tags to each cached item. Each tag is a plain string identifier that you can use at any time to trigger the removal of all items associated with this tag. diff --git a/components/cache/cache_pools.rst b/components/cache/cache_pools.rst index 7e9586dd582..0257d2bafcd 100644 --- a/components/cache/cache_pools.rst +++ b/components/cache/cache_pools.rst @@ -140,20 +140,11 @@ when all items are successfully deleted):: # clears the "cache.validation" and "cache.app" pool $ php bin/console cache:pool:clear cache.validation cache.app - .. versionadded:: 3.4 - Starting from Symfony 3.4, the ``cache:clear`` command no longer clears - the cache pools, so you must use the ``cache:pool:clear`` command to - delete them. - .. _component-cache-cache-pool-prune: Pruning Cache Items ------------------- -.. versionadded:: 3.4 - - Cache adapter pruning functionality was introduced in Symfony 3.4. - Some cache pools do not include an automated mechanism for pruning expired cache items. For example, the :ref:`FilesystemAdaper ` cache does not remove expired cache items *until an item is explicitly requested and determined to diff --git a/components/class_loader.rst b/components/class_loader.rst index 5bdf0a9c732..f3268b1ca71 100644 --- a/components/class_loader.rst +++ b/components/class_loader.rst @@ -4,72 +4,9 @@ The ClassLoader Component ========================= - The ClassLoader component provides tools to autoload your classes and - cache their locations for performance. - .. caution:: - The ClassLoader component was deprecated in Symfony 3.3 and it will be - removed in 4.0. As an alternative, use any of the `class loading optimizations`_ - provided by Composer. - -Usage ------ - -Whenever you reference a class that has not been required or included yet, -PHP uses the `autoloading mechanism`_ to delegate the loading of a file -defining the class. Symfony provides three autoloaders, which are able to -load your classes: - -* :doc:`/components/class_loader/class_loader`: loads classes that follow - the `PSR-0`_ class naming standard; - -* :doc:`/components/class_loader/psr4_class_loader`: loads classes that follow - the `PSR-4`_ class naming standard; - -* :doc:`/components/class_loader/map_class_loader`: loads classes using - a static map from class name to file path. - -Additionally, the Symfony ClassLoader component ships with a wrapper class -which makes it possible -:doc:`to cache the results of a class loader `. - -When using the :doc:`Debug component `, you -can also use a special :ref:`DebugClassLoader ` -that eases debugging by throwing more helpful exceptions when a class could -not be found by a class loader. - -Installation ------------- - -.. code-block:: terminal - - $ composer require symfony/class-loader - -Alternatively, you can clone the ``_ repository. - -.. include:: /components/require_autoload.rst.inc - -Learn More ----------- - -.. toctree:: - :glob: - :maxdepth: 1 - - class_loader/class_loader - class_loader/class_map_generator.rst - class_loader/debug_class_loader.rst - class_loader/map_class_loader.rst - class_loader/psr4_class_loader.rst - -.. toctree:: - :hidden: - - class_loader/cache_class_loader + The ClassLoader component was removed in Symfony 4.0. As an alternative, use + any of the `class loading optimizations`_ provided by Composer. -.. _PSR-0: https://www.php-fig.org/psr/psr-0/ -.. _PSR-4: https://www.php-fig.org/psr/psr-4/ -.. _`autoloading mechanism`: https://php.net/manual/en/language.oop5.autoload.php -.. _Packagist: https://packagist.org/packages/symfony/class-loader .. _`class loading optimizations`: https://getcomposer.org/doc/articles/autoloader-optimization.md diff --git a/components/class_loader/cache_class_loader.rst b/components/class_loader/cache_class_loader.rst deleted file mode 100644 index a75f743826c..00000000000 --- a/components/class_loader/cache_class_loader.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. index:: - single: APC; ApcClassLoader - single: ClassLoader; ApcClassLoader - single: ClassLoader; Cache - single: ClassLoader; XcacheClassLoader - single: XCache; XcacheClassLoader - -Cache a Class Loader -==================== - -The ``ApcClassLoader``, the ``WinCacheClassLoader`` and the ``XcacheClassLoader`` -are deprecated since Symfony 3.3. As an alternative, use any of the -`class loading optimizations`_ provided by Composer. - -.. _`class loading optimizations`: https://getcomposer.org/doc/articles/autoloader-optimization.md diff --git a/components/class_loader/class_loader.rst b/components/class_loader/class_loader.rst deleted file mode 100644 index c6d37661a84..00000000000 --- a/components/class_loader/class_loader.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. index:: - single: ClassLoader; PSR-0 Class Loader - -The PSR-0 Class Loader -====================== - -If your classes and third-party libraries follow the `PSR-0`_ standard, -you can use the :class:`Symfony\\Component\\ClassLoader\\ClassLoader` class -to load all of your project's classes. - -.. tip:: - - You can use both the ``ApcClassLoader`` and the ``XcacheClassLoader`` - to :doc:`cache ` a ``ClassLoader`` - instance. - -Usage ------ - -Registering the :class:`Symfony\\Component\\ClassLoader\\ClassLoader` autoloader -is straightforward:: - - require_once '/path/to/src/Symfony/Component/ClassLoader/ClassLoader.php'; - - use Symfony\Component\ClassLoader\ClassLoader; - - $loader = new ClassLoader(); - - // to enable searching the include path (eg. for PEAR packages) - $loader->setUseIncludePath(true); - - // ... register namespaces and prefixes here - see below - - $loader->register(); - -Use :method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefix` or -:method:`Symfony\\Component\\ClassLoader\\ClassLoader::addPrefixes` to register -your classes:: - - // register a single namespaces - $loader->addPrefix('Symfony', __DIR__.'/vendor/symfony/symfony/src'); - - // registers several namespaces at once - $loader->addPrefixes(array( - 'Symfony' => __DIR__.'/../vendor/symfony/symfony/src', - 'Monolog' => __DIR__.'/../vendor/monolog/monolog/src', - )); - - // registers a prefix for a class following the PEAR naming conventions - $loader->addPrefix('Twig_', __DIR__.'/vendor/twig/twig/lib'); - - $loader->addPrefixes(array( - 'Swift_' => __DIR__.'/vendor/swiftmailer/swiftmailer/lib/classes', - 'Twig_' => __DIR__.'/vendor/twig/twig/lib', - )); - -Classes from a sub-namespace or a sub-hierarchy of `PEAR`_ classes can be -looked for in a location list to ease the vendoring of a sub-set of classes -for large projects:: - - $loader->addPrefixes(array( - 'Doctrine\Common' => __DIR__.'/vendor/doctrine/common/lib', - 'Doctrine\DBAL\Migrations' => __DIR__.'/vendor/doctrine/migrations/lib', - 'Doctrine\DBAL' => __DIR__.'/vendor/doctrine/dbal/lib', - 'Doctrine' => __DIR__.'/vendor/doctrine/orm/lib', - )); - -In this example, if you try to use a class in the ``Doctrine\Common`` namespace -or one of its children, the autoloader will first look for the class under -the ``doctrine-common`` directory. If not found, it will then fallback to -the default ``Doctrine`` directory (the last one configured) before giving -up. The order of the prefix registrations is significant in this case. - -.. _PEAR: https://pear.php.net/manual/en/standards.naming.php -.. _PSR-0: https://www.php-fig.org/psr/psr-0/ diff --git a/components/class_loader/class_map_generator.rst b/components/class_loader/class_map_generator.rst deleted file mode 100644 index c060cc71a72..00000000000 --- a/components/class_loader/class_map_generator.rst +++ /dev/null @@ -1,128 +0,0 @@ -.. index:: - single: Autoloading; Class Map Generator - single: ClassLoader; Class Map Generator - -The Class Map Generator -======================= - -Loading a class usually is an easy task given the `PSR-0`_ and `PSR-4`_ -standards. Thanks to the Symfony ClassLoader component or the autoloading -mechanism provided by Composer, you don't have to map your class names to -actual PHP files manually. Nowadays, PHP libraries usually come with autoloading -support through Composer. - -But from time to time you may have to use a third-party library that comes -without any autoloading support and therefore forces you to load each class -manually. For example, imagine a library with the following directory structure: - -.. code-block:: text - - library/ - ├── bar/ - │   ├── baz/ - │   │   └── Boo.php - │   └── Foo.php - └── foo/ - ├── bar/ - │   └── Foo.php - └── Bar.php - -These files contain the following classes: - -=========================== ================ -File Class Name -=========================== ================ -``library/bar/baz/Boo.php`` ``Acme\Bar\Baz`` -``library/bar/Foo.php`` ``Acme\Bar`` -``library/foo/bar/Foo.php`` ``Acme\Foo\Bar`` -``library/foo/Bar.php`` ``Acme\Foo`` -=========================== ================ - -To make your life easier, the ClassLoader component comes with a -:class:`Symfony\\Component\\ClassLoader\\ClassMapGenerator` class that makes -it possible to create a map of class names to files. - -Generating a Class Map ----------------------- - -To generate the class map, simply pass the root directory of your class -files to the -:method:`Symfony\\Component\\ClassLoader\\ClassMapGenerator::createMap` -method:: - - use Symfony\Component\ClassLoader\ClassMapGenerator; - - var_dump(ClassMapGenerator::createMap(__DIR__.'/library')); - -Given the files and class from the table above, you should see an output -like this: - -.. code-block:: text - - Array - ( - [Acme\Foo] => /var/www/library/foo/Bar.php - [Acme\Foo\Bar] => /var/www/library/foo/bar/Foo.php - [Acme\Bar\Baz] => /var/www/library/bar/baz/Boo.php - [Acme\Bar] => /var/www/library/bar/Foo.php - ) - -Dumping the Class Map ---------------------- - -Writing the class map to the console output is not really sufficient when -it comes to autoloading. Luckily, the ``ClassMapGenerator`` provides the -:method:`Symfony\\Component\\ClassLoader\\ClassMapGenerator::dump` method -to save the generated class map to the filesystem:: - - use Symfony\Component\ClassLoader\ClassMapGenerator; - - ClassMapGenerator::dump(__DIR__.'/library', __DIR__.'/class_map.php'); - -This call to ``dump()`` generates the class map and writes it to the ``class_map.php`` -file in the same directory with the following contents:: - - '/var/www/library/foo/Bar.php', - 'Acme\\Foo\\Bar' => '/var/www/library/foo/bar/Foo.php', - 'Acme\\Bar\\Baz' => '/var/www/library/bar/baz/Boo.php', - 'Acme\\Bar' => '/var/www/library/bar/Foo.php', - ); - -Instead of loading each file manually, you'll only have to register the -generated class map with, for example, the -:class:`Symfony\\Component\\ClassLoader\\MapClassLoader`:: - - use Symfony\Component\ClassLoader\MapClassLoader; - - $mapping = include __DIR__.'/class_map.php'; - $loader = new MapClassLoader($mapping); - $loader->register(); - - // you can now use the classes: - use Acme\Foo; - - $foo = new Foo(); - - // ... - -.. note:: - - The example assumes that you already have autoloading working (e.g. - through `Composer`_ or one of the other class loaders from the ClassLoader - component. - -Besides dumping the class map for one directory, you can also pass an array -of directories for which to generate the class map (the result actually -is the same as in the example above):: - - use Symfony\Component\ClassLoader\ClassMapGenerator; - - ClassMapGenerator::dump( - array(__DIR__.'/library/bar', __DIR__.'/library/foo'), - __DIR__.'/class_map.php' - ); - -.. _`PSR-0`: https://www.php-fig.org/psr/psr-0 -.. _`PSR-4`: https://www.php-fig.org/psr/psr-4 -.. _`Composer`: https://getcomposer.org diff --git a/components/class_loader/debug_class_loader.rst b/components/class_loader/debug_class_loader.rst deleted file mode 100644 index 1bd3446af27..00000000000 --- a/components/class_loader/debug_class_loader.rst +++ /dev/null @@ -1,8 +0,0 @@ -Debugging a Class Loader -======================== - -.. caution:: - - The ``DebugClassLoader`` from the ClassLoader component was deprecated - in Symfony 2.5 and removed in Symfony 3.0. Use the - :ref:`DebugClassLoader provided by the Debug component `. diff --git a/components/class_loader/map_class_loader.rst b/components/class_loader/map_class_loader.rst deleted file mode 100644 index e6e464e771e..00000000000 --- a/components/class_loader/map_class_loader.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. index:: - single: ClassLoader; MapClassLoader - -MapClassLoader -============== - -The :class:`Symfony\\Component\\ClassLoader\\MapClassLoader` allows you -to autoload files via a static map from classes to files. This is useful -if you use third-party libraries which don't follow the `PSR-0`_ standards -and so can't use the -:doc:`PSR-0 class loader `. - -The ``MapClassLoader`` can be used along with the -:doc:`PSR-0 class loader ` by -configuring and calling the ``register()`` method on both. - -.. note:: - - The default behavior is to append the ``MapClassLoader`` on the autoload - stack. If you want to use it as the first autoloader, pass ``true`` - when calling the ``register()`` method. Your class loader will then - be prepended on the autoload stack. - -Usage ------ - -Using it is as easy as passing your mapping to its constructor when creating -an instance of the ``MapClassLoader`` class:: - - require_once '/path/to/src/Symfony/Component/ClassLoader/MapClassLoader.php'; - - $mapping = array( - 'Foo' => '/path/to/Foo', - 'Bar' => '/path/to/Bar', - ); - - $loader = new MapClassLoader($mapping); - - $loader->register(); - -.. _PSR-0: https://www.php-fig.org/psr/psr-0/ diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst deleted file mode 100644 index 09d0af057e9..00000000000 --- a/components/class_loader/psr4_class_loader.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. index:: - single: ClassLoader; PSR-4 Class Loader - -The PSR-4 Class Loader -====================== - -Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLoader``. - -.. note:: - - If you manage your dependencies via Composer, you get a PSR-4 compatible - autoloader out of the box. Use this loader in environments where Composer - is not available. - -.. tip:: - - All Symfony components follow PSR-4. - -Usage ------ - -The following example demonstrates how you can use the -:class:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader` autoloader to use -Symfony's Yaml component. Imagine, you downloaded both the ClassLoader and -Yaml component as ZIP packages and unpacked them to a ``libs`` directory. -The directory structure will look like this: - -.. code-block:: text - - libs/ - ClassLoader/ - Psr4ClassLoader.php - ... - Yaml/ - Yaml.php - ... - config.yml - demo.php - -In ``demo.php`` you are going to parse the ``config.yml`` file. To do that, you -first need to configure the ``Psr4ClassLoader``:: - - use Symfony\Component\ClassLoader\Psr4ClassLoader; - use Symfony\Component\Yaml\Yaml; - - require __DIR__.'/lib/ClassLoader/Psr4ClassLoader.php'; - - $loader = new Psr4ClassLoader(); - $loader->addPrefix('Symfony\Component\Yaml\\', __DIR__.'/lib/Yaml'); - $loader->register(); - - $data = Yaml::parse(file_get_contents(__DIR__.'/config.yml')); - -First of all, the class loader is loaded manually using a ``require`` -statement, since there is no autoload mechanism yet. With the -:method:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader::addPrefix` call, you -tell the class loader where to look for classes with the -``Symfony\Component\Yaml\`` namespace prefix. After registering the autoloader, -the Yaml component is ready to be used. - -.. _PSR-4: https://www.php-fig.org/psr/psr-4/ diff --git a/components/config/caching.rst b/components/config/caching.rst index 99cd4f6a2b1..7881306a4b8 100644 --- a/components/config/caching.rst +++ b/components/config/caching.rst @@ -32,7 +32,7 @@ should be regenerated:: $userMatcherCache = new ConfigCache($cachePath, true); if (!$userMatcherCache->isFresh()) { - // fill this with an array of 'users.yml' file paths + // fill this with an array of 'users.yaml' file paths $yamlUserFiles = ...; $resources = array(); diff --git a/components/config/definition.rst b/components/config/definition.rst index 5d0edc7e183..6f30b7b026a 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -186,20 +186,11 @@ Or you may define a prototype for each node inside an array node:: ->end() ; -.. versionadded:: 3.3 - The ``arrayPrototype()`` method (and the related ``booleanPrototype()`` - ``integerPrototype()``, ``floatPrototype()``, ``scalarPrototype()`` and - ``enumPrototype()``) was introduced in Symfony 3.3. In previous versions, - you needed to use ``prototype('array')``, ``prototype('boolean')``, etc. - A prototype can be used to add a definition which may be repeated many times inside the current node. According to the prototype definition in the example above, it is possible to have multiple connection arrays (containing a ``driver``, ``host``, etc.). -.. versionadded:: 3.3 - The ``castToArray()`` helper was added in Symfony 3.3. - Sometimes, to improve the user experience of your application or bundle, you may allow to use a simple string or numeric value where an array value is required. Use the ``castToArray()`` helper to turn those variables into arrays:: @@ -795,6 +786,48 @@ A validation rule also requires a "then" part: Usually, "then" is a closure. Its return value will be used as a new value for the node, instead of the node's original value. +Configuring the Node Path Separator +----------------------------------- + +.. versionadded:: 4.1 + The option to configure the node path separator was introduced in Symfony 4.1. + +Consider the following config builder example:: + + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('database'); + + $rootNode + ->children() + ->arrayNode('connection') + ->children() + ->scalarNode('driver')->end() + ->end() + ->end() + ->end() + ; + +By default, the hierarchy of nodes in a config path is defined with a dot +character (``.``):: + + // ... + + $node = $treeBuilder->buildTree(); + $children = $node->getChildren(); + $path = $children['driver']->getPath(); + // $path = 'database.connection.driver' + +Use the ``setPathSeparator()`` method on the config builder to change the path +separator:: + + // ... + + $treeBuilder->setPathSeparator('/'); + $node = $treeBuilder->buildTree(); + $children = $node->getChildren(); + $path = $children['driver']->getPath(); + // $path = 'database/connection/driver' + Processing Configuration Values ------------------------------- @@ -811,10 +844,10 @@ Otherwise the result is a clean array of configuration values:: use Acme\DatabaseConfiguration; $config = Yaml::parse( - file_get_contents(__DIR__.'/src/Matthias/config/config.yml') + file_get_contents(__DIR__.'/src/Matthias/config/config.yaml') ); $extraConfig = Yaml::parse( - file_get_contents(__DIR__.'/src/Matthias/config/config_extra.yml') + file_get_contents(__DIR__.'/src/Matthias/config/config_extra.yaml') ); $configs = array($config, $extraConfig); diff --git a/components/config/resources.rst b/components/config/resources.rst index 9b464a6a0cc..6c7a12607f0 100644 --- a/components/config/resources.rst +++ b/components/config/resources.rst @@ -19,10 +19,10 @@ files. This can be done with the :class:`Symfony\\Component\\Config\\FileLocator use Symfony\Component\Config\FileLocator; - $configDirectories = array(__DIR__.'/app/config'); + $configDirectories = array(__DIR__.'/config'); $fileLocator = new FileLocator($configDirectories); - $yamlUserFiles = $fileLocator->locate('users.yml', null, false); + $yamlUserFiles = $fileLocator->locate('users.yaml', null, false); The locator receives a collection of locations where it should look for files. The first argument of ``locate()`` is the name of the file to look @@ -53,12 +53,12 @@ which allows for recursively importing other resources:: // maybe import some other resource: - // $this->import('extra_users.yml'); + // $this->import('extra_users.yaml'); } public function supports($resource, $type = null) { - return is_string($resource) && 'yml' === pathinfo( + return is_string($resource) && 'yaml' === pathinfo( $resource, PATHINFO_EXTENSION ); @@ -88,5 +88,5 @@ the resource:: $delegatingLoader = new DelegatingLoader($loaderResolver); // YamlUserLoader is used to load this resource because it supports - // files with the '.yml' extension - $delegatingLoader->load(__DIR__.'/users.yml'); + // files with the '.yaml' extension + $delegatingLoader->load(__DIR__.'/users.yaml'); diff --git a/components/console/events.rst b/components/console/events.rst index 2c3b69f5492..0aaad7abb29 100644 --- a/components/console/events.rst +++ b/components/console/events.rst @@ -83,27 +83,24 @@ C/C++ standard.:: } }); -The ``ConsoleEvents::EXCEPTION`` Event --------------------------------------- - -.. versionadded:: 3.3 - The ``ConsoleEvents::EXCEPTION`` event was deprecated in Symfony 3.3. Use - the ``ConsoleEvents::ERROR`` event instead. +The ``ConsoleEvents::ERROR`` Event +---------------------------------- **Typical Purposes**: Handle exceptions thrown during the execution of a command. -Whenever an exception is thrown by a command, the ``ConsoleEvents::EXCEPTION`` -event is dispatched. A listener can wrap or change the exception or do -anything useful before the exception is thrown by the application. +Whenever an exception is thrown by a command, including those triggered from +event listeners, the ``ConsoleEvents::ERROR`` event is dispatched. A listener +can wrap or change the exception or do anything useful before the exception is +thrown by the application. Listeners receive a :class:`Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent` event:: - use Symfony\Component\Console\Event\ConsoleExceptionEvent; + use Symfony\Component\Console\Event\ConsoleErrorEvent; use Symfony\Component\Console\ConsoleEvents; - $dispatcher->addListener(ConsoleEvents::EXCEPTION, function (ConsoleExceptionEvent $event) { + $dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) { $output = $event->getOutput(); $command = $event->getCommand(); @@ -114,22 +111,9 @@ Listeners receive a $exitCode = $event->getExitCode(); // changes the exception to another one - $event->setException(new \LogicException('Caught exception', $exitCode, $event->getException())); + $event->setException(new \LogicException('Caught exception', $exitCode, $event->getError())); }); -The ``ConsoleEvents::ERROR`` Event ----------------------------------- - -.. versionadded:: 3.3 - The ``ConsoleEvents::ERROR`` event was introduced in Symfony 3.3. - -**Typical Purposes**: Handle exceptions thrown during the execution of a -command. - -This event is an improved version of the ``ConsoleEvents::EXCEPTION`` event, -because it can handle every exception thrown during the execution of a command, -including those triggered from event listeners. - The ``ConsoleEvents::TERMINATE`` Event -------------------------------------- diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst deleted file mode 100644 index 8618ffcba17..00000000000 --- a/components/console/helpers/dialoghelper.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. index:: - single: Console Helpers; Dialog Helper - -Dialog Helper -============= - -.. caution:: - - The Dialog Helper was deprecated in Symfony 2.5 and removed in - Symfony 3.0. You should now use the - :doc:`Question Helper ` instead, - which is simpler to use. diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 38a8c2a7939..87c62ca7629 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -7,7 +7,6 @@ The Console Helpers .. toctree:: :hidden: - dialoghelper formatterhelper processhelper progressbar diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index aa1aa53ea21..5b3cdec56bd 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -55,9 +55,20 @@ you can also set the current progress by calling the accordingly. By default, when using a ``max``, the redraw frequency is set to *10%* of your ``max``. -If you don't know the number of steps in advance, just omit the steps argument -when creating the :class:`Symfony\\Component\\Console\\Helper\\ProgressBar` -instance:: +If you don't know the exact number of steps in advance, set it to a reasonable +value and then call the ``setMaxSteps()`` method to update it as needed:: + + // start with a 50 units progressbar + $progressBar = new ProgressBar($output, 50); + + // a complex task has just been created: increase the progressbar to 200 units + $progressBar->setMaxSteps(200); + +.. versionadded:: 4.1 + The ``setMaxSteps()`` method was introduced in Symfony 4.1. + +Another solution is to just omit the steps argument when creating the +:class:`Symfony\\Component\\Console\\Helper\\ProgressBar` instance:: $progressBar = new ProgressBar($output); diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index 2e25070381e..749e3def7b2 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -229,9 +229,6 @@ convenient for passwords:: // ... } - .. versionadded:: 3.3 - The ``QuestionHelper::disableStty()`` method was introduced in Symfony 3.3. - Normalizing the Answer ---------------------- @@ -250,7 +247,7 @@ method:: // ... $helper = $this->getHelper('question'); - $question = new Question('Please enter the name of the bundle', 'AppBundle'); + $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle'); $question->setNormalizer(function ($value) { // $value can be null here return $value ? trim($value) : ''; @@ -368,9 +365,6 @@ from the command line, you need to set the inputs that the command expects:: // $this->assertRegExp('/.../', $commandTester->getDisplay()); } -.. versionadded:: 3.2 - The ``CommandTester::setInputs()`` method was introduced in Symfony 3.2. - By calling :method:`Symfony\\Component\\Console\\Tester\\CommandTester::setInputs`, you imitate what the console would do internally with all user input through the CLI. This method takes an array as only argument with, for each input that the command expects, diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst index 9a8ec0ed259..3129e8eb2d9 100644 --- a/components/console/helpers/table.rst +++ b/components/console/helpers/table.rst @@ -143,6 +143,48 @@ which outputs: 80-902734-1-6 And Then There Were None Agatha Christie =============== ========================== ================== +You can also set the style to ``box``:: + + $table->setStyle('box'); + $table->render(); + +which outputs: + +.. code-block:: text + + ┌───────────────┬──────────────────────────┬──────────────────┐ + │ ISBN │ Title │ Author │ + ├───────────────┼──────────────────────────┼──────────────────┤ + │ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri │ + │ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens │ + │ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien │ + │ 80-902734-1-6 │ And Then There Were None │ Agatha Christie │ + └───────────────┴──────────────────────────┴──────────────────┘ + +.. versionadded:: 4.1 + The ``box`` style was introduced in Symfony 4.1. + +You can also set the style to ``box-double``:: + + $table->setStyle('box-double'); + $table->render(); + +which outputs: + +.. code-block:: text + + ╔═══════════════╤══════════════════════════╤══════════════════╗ + ║ ISBN │ Title │ Author ║ + ╠═══════════════╪══════════════════════════╪══════════════════╣ + ║ 99921-58-10-7 │ Divine Comedy │ Dante Alighieri ║ + ║ 9971-5-0210-0 │ A Tale of Two Cities │ Charles Dickens ║ + ║ 960-425-059-0 │ The Lord of the Rings │ J. R. R. Tolkien ║ + ║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║ + ╚═══════════════╧══════════════════════════╧══════════════════╝ + +.. versionadded:: 4.1 + The ``box-double`` style was introduced in Symfony 4.1. + If the built-in styles do not fit your need, define your own:: use Symfony\Component\Console\Helper\TableStyle; @@ -152,9 +194,9 @@ If the built-in styles do not fit your need, define your own:: // customizes the style $tableStyle - ->setHorizontalBorderChar('|') - ->setVerticalBorderChar('-') - ->setCrossingChar(' ') + ->setDefaultCrossingChars('|') + ->setVerticalBorderChars('-') + ->setDefaultCrossingChar(' ') ; // uses the custom style for this table @@ -163,14 +205,26 @@ If the built-in styles do not fit your need, define your own:: Here is a full list of things you can customize: * :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setPaddingChar` -* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setHorizontalBorderChar` -* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setVerticalBorderChar` -* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCrossingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setDefaultCrossingChars` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setVerticalBorderChars` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCrossingChars` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setDefaultCrossingChar` * :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCellHeaderFormat` * :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCellRowFormat` * :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setBorderFormat` * :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setPadType` +.. versionadded:: 4.1 + The ``setDefaultCrossingChars`` method was introduced in Symfony 4.1. + It replaces the deprecated ``setHorizontalBorderChar`` method. + + Also, the ``setVerticalBorderChars`` method was introduced. Use this instead + of the deprecated ``setVerticalBorderChar`` method. + + The ``setCrossingChars()`` and ``setDefaultCrossingChar()`` methods are also + new. Previously you could only use the now deprecated ``setCrossingChar()`` + method. + .. tip:: You can also register a style globally:: diff --git a/components/console/logger.rst b/components/console/logger.rst index e3c7140b8b4..164d5db8da6 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -106,9 +106,6 @@ constructor:: Errors ------ -.. versionadded:: 3.2 - The ``hasErrored()`` method was introduced in Symfony 3.2. - The Console logger includes a ``hasErrored()`` method which returns ``true`` as soon as any error message has been logged during the execution of the command. This is useful to decide which status code to return as the result of executing diff --git a/components/console/usage.rst b/components/console/usage.rst index bf77b3d6e91..f8a7c0283df 100644 --- a/components/console/usage.rst +++ b/components/console/usage.rst @@ -156,8 +156,3 @@ can run it with: If you enter a short command that's ambiguous (i.e. there are more than one command that match), then no command will be run and some suggestions of the possible commands to choose from will be output. - -.. versionadded:: 3.4 - Case-insensitivity of command shortcuts was introduced in Symfony 3.4. In - previous Symfony versions, shortcuts had to match the case of the original - command name (e.g. ``d:g`` was not the same shortcut as ``D:G``). diff --git a/components/dependency_injection.rst b/components/dependency_injection.rst index 57204767dcd..4a3c709947a 100644 --- a/components/dependency_injection.rst +++ b/components/dependency_injection.rst @@ -210,7 +210,7 @@ Loading a YAML config file:: $containerBuilder = new ContainerBuilder(); $loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__)); - $loader->load('services.yml'); + $loader->load('services.yaml'); .. note:: diff --git a/components/dependency_injection/_imports-parameters-note.rst.inc b/components/dependency_injection/_imports-parameters-note.rst.inc index bcaff4c1bfc..eb68a4dbdcc 100644 --- a/components/dependency_injection/_imports-parameters-note.rst.inc +++ b/components/dependency_injection/_imports-parameters-note.rst.inc @@ -8,13 +8,13 @@ .. code-block:: yaml - # app/config/config.yml + # config/services.yaml imports: - - { resource: '%kernel.project_dir%/app/parameters.yml' } + - { resource: '%kernel.project_dir%/somefile.yaml' } .. code-block:: xml - + - + .. code-block:: php - // app/config/config.php - $loader->import('%kernel.project_dir%/app/parameters.yml'); + // config/services.php + $loader->import('%kernel.project_dir%/somefile.yaml'); diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index b5c3e9a996d..c15cf5fb27c 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -121,7 +121,7 @@ are loaded:: $containerBuilder->registerExtension(new AcmeDemoExtension); $loader = new YamlFileLoader($containerBuilder, new FileLocator(__DIR__)); - $loader->load('config.yml'); + $loader->load('config.yaml'); // ... $containerBuilder->compile(); @@ -423,9 +423,6 @@ been run, use:: PassConfig::TYPE_AFTER_REMOVING ); -.. versionadded:: 3.2 - The option to prioritize compiler passes was added in Symfony 3.2. - You can also control the order in which compiler passes are run for each compilation phase. Use the optional third argument of ``addCompilerPass()`` to set the priority as an integer number. The default priority is ``0`` and the higher diff --git a/components/dependency_injection/workflow.rst b/components/dependency_injection/workflow.rst index 5ad8ae7c3d8..750420f4d47 100644 --- a/components/dependency_injection/workflow.rst +++ b/components/dependency_injection/workflow.rst @@ -34,7 +34,7 @@ for more details. Application-level Configuration ------------------------------- -Application level config is loaded from the ``app/config`` directory. Multiple +Application level config is loaded from the ``config`` directory. Multiple files are loaded which are then merged when the extensions are processed. This allows for different configuration for different environments e.g. dev, prod. diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 4d80f64cae4..7212b635835 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -247,11 +247,6 @@ The crawler supports multiple ways of adding the content:: guesses the best charset according to the given contents and defaults to ``ISO-8859-1`` in case no charset can be guessed. - .. versionadded:: 3.4 - The charset guessing mechanism of the ``addContent()`` method was - introduced in Symfony 3.4. In previous Symfony versions, the ``ISO-8859-1`` - charset was always used. - As the Crawler's implementation is based on the DOM extension, it is also able to interact with native :phpclass:`DOMDocument`, :phpclass:`DOMNodeList` and :phpclass:`DOMNode` objects:: @@ -293,10 +288,6 @@ and :phpclass:`DOMNode` objects:: Expression Evaluation ~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.2 - The :method:`Symfony\\Component\\DomCrawler\\Crawler::evaluate` method was - introduced in Symfony 3.2. - The ``evaluate()`` method evaluates the given XPath expression. The return value depends on the XPath expression. If the expression evaluates to a scalar value (e.g. HTML attributes), an array of results will be returned. If the @@ -432,11 +423,12 @@ than just return the ``action`` attribute of the form. If the form method is GET, then it mimics the browser's behavior and returns the ``action`` attribute followed by a query string of all of the form's values. -.. versionadded:: 3.3 - Starting from Symfony 3.3, the optional ``formaction`` and ``formmethod`` - button attributes are supported. The ``getUri()`` and ``getMethod()`` - methods take into account those attributes to always return the right action - and method depending on the button used to get the form. +.. note:: + + The optional ``formaction`` and ``formmethod`` button attributes are + supported. The ``getUri()`` and ``getMethod()`` methods take into account + those attributes to always return the right action and method depending on + the button used to get the form. You can virtually set and get values on the form:: diff --git a/components/dotenv.rst b/components/dotenv.rst index 040eecc1633..fae35a5b5f2 100644 --- a/components/dotenv.rst +++ b/components/dotenv.rst @@ -8,9 +8,6 @@ The Dotenv Component The Dotenv Component parses ``.env`` files to make environment variables stored in them accessible via ``getenv()``, ``$_ENV`` or ``$_SERVER``. -.. versionadded:: 3.3 - The Dotenv component was introduced in Symfony 3.3. - Installation ------------ diff --git a/components/event_dispatcher/container_aware_dispatcher.rst b/components/event_dispatcher/container_aware_dispatcher.rst index f6192d11020..659a94cee7a 100644 --- a/components/event_dispatcher/container_aware_dispatcher.rst +++ b/components/event_dispatcher/container_aware_dispatcher.rst @@ -4,103 +4,7 @@ The Container Aware Event Dispatcher ==================================== -.. versionadded:: 3.3 - The ``ContainerAwareEventDispatcher`` class has been deprecated in Symfony 3.3 - and it will be removed in Symfony 4.0. Use ``EventDispatcher`` with - closure-proxy injection instead. +.. caution:: -Introduction ------------- - -The :class:`Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher` -is a special ``EventDispatcher`` implementation which is coupled to the -service container that is part of -:doc:`the DependencyInjection component `. -It allows services to be specified as event listeners making the ``EventDispatcher`` -extremely powerful. - -Services are lazy loaded meaning the services attached as listeners will -only be created if an event is dispatched that requires those listeners. - -Setup ------ - -Setup is straightforward by injecting a :class:`Symfony\\Component\\DependencyInjection\\ContainerInterface` -into the :class:`Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher`:: - - use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; - - $containerBuilder = new ContainerBuilder(); - $dispatcher = new ContainerAwareEventDispatcher($containerBuilder); - -Adding Listeners ----------------- - -The ``ContainerAwareEventDispatcher`` can either load specified services -directly or services that implement :class:`Symfony\\Component\\EventDispatcher\\EventSubscriberInterface`. - -The following examples assume the service container has been loaded with -any services that are mentioned. - -.. note:: - - Services must be marked as public in the container. - -Adding Services -~~~~~~~~~~~~~~~ - -To connect existing service definitions, use the -:method:`Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher::addListenerService` -method where the ``$callback`` is an array of ``array($serviceId, $methodName)``:: - - $dispatcher->addListenerService($eventName, array('foo', 'logListener')); - -Adding Subscriber Services -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Event subscribers can be added using the -:method:`Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher::addSubscriberService` -method where the first argument is the service ID of the subscriber service, -and the second argument is the service's class name (which must implement -:class:`Symfony\\Component\\EventDispatcher\\EventSubscriberInterface`) as follows:: - - $dispatcher->addSubscriberService( - 'kernel.store_subscriber', - 'StoreSubscriber' - ); - -The ``EventSubscriberInterface`` is exactly as you would expect:: - - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Symfony\Component\HttpKernel\KernelEvents; - // ... - - class StoreSubscriber implements EventSubscriberInterface - { - public static function getSubscribedEvents() - { - return array( - KernelEvents::RESPONSE => array( - array('onKernelResponsePre', 10), - array('onKernelResponsePost', 0), - ), - 'store.order' => array('onStoreOrder', 0), - ); - } - - public function onKernelResponsePre(FilterResponseEvent $event) - { - // ... - } - - public function onKernelResponsePost(FilterResponseEvent $event) - { - // ... - } - - public function onStoreOrder(FilterOrderEvent $event) - { - // ... - } - } + The ``ContainerAwareEventDispatcher`` was removed in Symfony 4.0. Use + ``EventDispatcher`` with closure-proxy injection instead. diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst index 07c8539c7bc..0cf863283ac 100644 --- a/components/expression_language/caching.rst +++ b/components/expression_language/caching.rst @@ -37,12 +37,6 @@ ones and injecting this using the constructor:: $cache = new RedisAdapter(...); $expressionLanguage = new ExpressionLanguage($cache); -.. versionadded:: 3.2 - PSR-6 caching support was introduced in Symfony 3.2. Prior to version 3.2, - a - :class:`Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheInterface` - instance had to be injected. - .. seealso:: See the :doc:`/components/cache` documentation for more information about diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 808aefad467..d97ed6ac588 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -100,9 +100,6 @@ register:: ExpressionFunction::fromPhp('My\strtoupper', 'my_strtoupper'); - .. versionadded:: 3.3 - The ``ExpressionFunction::fromPhp()`` method was introduced in Symfony 3.3. - You can register providers using :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::registerProvider` or by using the second argument of the constructor:: @@ -123,12 +120,12 @@ or by using the second argument of the constructor:: It is recommended to create your own ``ExpressionLanguage`` class in your library. Now you can add the extension by overriding the constructor:: + use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; - use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; class ExpressionLanguage extends BaseExpressionLanguage { - public function __construct(ParserCacheInterface $parser = null, array $providers = array()) + public function __construct(CacheItemPoolInterface $parser = null, array $providers = array()) { // prepends the default provider to let users override it easily array_unshift($providers, new StringExpressionLanguageProvider()); diff --git a/components/filesystem.rst b/components/filesystem.rst index 0747270c08e..0f690fd8dc3 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -211,9 +211,6 @@ support symbolic links, a third boolean argument is available:: readlink ~~~~~~~~ -.. versionadded:: 3.2 - The :method:`Symfony\\Component\\Filesystem\\Filesystem::readlink` method was introduced in Symfony 3.2. - :method:`Symfony\\Component\\Filesystem\\Filesystem::readlink` read links targets. PHP's ``readlink()`` function returns the target of a symbolic link. However, its behavior @@ -297,10 +294,6 @@ The ``file.txt`` file contains ``Hello World`` now. appendToFile ~~~~~~~~~~~~ -.. versionadded:: 3.3 - The :method:`Symfony\\Component\\Filesystem\\Filesystem::appendToFile` - method was introduced in Symfony 3.3. - :method:`Symfony\\Component\\Filesystem\\Filesystem::appendToFile` adds new contents at the end of some file:: @@ -321,14 +314,5 @@ Whenever something wrong happens, an exception implementing An :class:`Symfony\\Component\\Filesystem\\Exception\\IOException` is thrown if directory creation fails. -Learn More ----------- - -.. toctree:: - :maxdepth: 1 - :glob: - - filesystem/* - .. _`Packagist`: https://packagist.org/packages/symfony/filesystem .. _`umask`: https://en.wikipedia.org/wiki/Umask diff --git a/components/filesystem/lock_handler.rst b/components/filesystem/lock_handler.rst index 1f5cb56fd3e..e7dab2fa625 100644 --- a/components/filesystem/lock_handler.rst +++ b/components/filesystem/lock_handler.rst @@ -1,95 +1,9 @@ +:orphan: + LockHandler =========== -.. versionadded:: 3.4 - The ``LockHandler`` class was deprecated in Symfony 3.4 and it will be - removed in Symfony 4.0. Use :ref:`SemaphoreStore ` - or :ref:`FlockStore ` from the Lock component instead. - -What is a Lock? ---------------- - -File locking is a mechanism that restricts access to a computer file by allowing -only one user or process access at any specific time. This mechanism was -introduced a few decades ago for mainframes, but continues being useful for -modern applications. - -Symfony provides a LockHelper to help you use locks in your project. - -Usage ------ - -.. caution:: - - The lock handler only works if you're using just one server. If you have - several hosts, you must not use this helper. - -A lock can be used, for example, to allow only one instance of a command to run:: - - use Symfony\Component\Filesystem\LockHandler; - - $lockHandler = new LockHandler('hello.lock'); - if (!$lockHandler->lock()) { - // the resource "hello" is already locked by another process - - return 0; - } - -The first argument of the constructor is a string that it will use as part of -the name of the file used to create the lock on the local filesystem. A best -practice for Symfony commands is to use the command name, such as ``acme:my-command``. -``LockHandler`` sanitizes the contents of the string before creating -the file, so you can pass any value for this argument. - -.. tip:: - - The ``.lock`` extension is optional, but it's a common practice to include - it. This will make it easier to find lock files on the filesystem. Moreover, - to avoid name collisions, ``LockHandler`` also appends a hash to the name of - the lock file. - -By default, the lock will be created in the system's temporary directory, but -you can optionally select the directory where locks are created by passing it as -the second argument of the constructor. - -.. tip:: - - Another way to configure the directory where the locks are created is to - define a special environment variable, because PHP will use that value to - override the default temporary directory. On Unix-based systems, define the - ``TMPDIR`` variable. On Windows systems, define any of these variables: - ``TMP``, ``TEMP`` or ``USERPROFILE`` (they are checked in this order). This - technique is useful for example when deploying a third-party Symfony - application whose code can't be modified. - -The :method:`Symfony\\Component\\Filesystem\\LockHandler::lock` method tries to -acquire the lock. If the lock is acquired, the method returns ``true``, -``false`` otherwise. If the ``lock()`` method is called several times on the same -instance it will always return ``true`` if the lock was acquired on the first -call. - -You can pass an optional blocking argument as the first argument to the -``lock()`` method, which defaults to ``false``. If this is set to ``true``, your -PHP code will wait indefinitely until the lock is released by another process. - .. caution:: - Be aware of the fact that the resource lock is automatically released as soon - as PHP applies the garbage-collection process to the ``LockHandler`` object. - This means that if you refactor the first example shown in this article as - follows:: - - use Symfony\Component\Filesystem\LockHandler; - - if (!(new LockHandler('hello.lock'))->lock()) { - // the resource "hello" is already locked by another process - - return 0; - } - - Now the code won't work as expected because PHP's garbage collection mechanism - removes the reference to the ``LockHandler`` object and thus, the lock is released - just after it's been created. - - Another alternative way to release the lock explicitly when needed is to use the - :method:`Symfony\\Component\\Filesystem\\LockHandler::release` method. + The ``LockHandler`` utility was removed in Symfony 4.0. Use the new Symfony + :doc:`Lock component ` instead. diff --git a/components/finder.rst b/components/finder.rst index aac75959293..790e0cf7bdc 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -80,9 +80,6 @@ There are lots of ways to filter and sort your results. You can also use the :method:`Symfony\\Component\\Finder\\Finder::hasResults` method to check if there's any file or directory matching the search criteria. -.. versionadded:: 3.4 - The ``hasResults()`` method was introduced in Symfony 3.4. - Location ~~~~~~~~ diff --git a/components/form.rst b/components/form.rst index dcf67b74f7b..8b4d83d8ac7 100644 --- a/components/form.rst +++ b/components/form.rst @@ -399,17 +399,17 @@ is created from the form factory. .. code-block:: php-symfony - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/Controller/TaskController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\DateType; - class DefaultController extends Controller + class TaskController extends Controller { - public function newAction(Request $request) + public function new(Request $request) { // createFormBuilder is a shortcut to get the "form factory" // and then call "createBuilder()" on it @@ -419,7 +419,7 @@ is created from the form factory. ->add('dueDate', DateType::class) ->getForm(); - return $this->render('@AcmeTask/Default/new.html.twig', array( + return $this->render('task/new.html.twig', array( 'form' => $form->createView(), )); } @@ -461,8 +461,8 @@ builder: .. code-block:: php-symfony - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/Controller/DefaultController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -470,7 +470,7 @@ builder: class DefaultController extends Controller { - public function newAction(Request $request) + public function new(Request $request) { $defaults = array( 'dueDate' => new \DateTime('tomorrow'), @@ -543,15 +543,15 @@ by ``handleRequest()`` to determine whether a form has been submitted): .. code-block:: php-symfony - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/Controller/DefaultController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Form\Extension\Core\Type\FormType; class DefaultController extends Controller { - public function searchAction() + public function search() { $formBuilder = $this->createFormBuilder(null, array( 'action' => '/search', @@ -605,16 +605,16 @@ method: .. code-block:: php-symfony - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/Controller/TaskController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Form\Extension\Core\Type\DateType; use Symfony\Component\Form\Extension\Core\Type\TextType; - class DefaultController extends Controller + class TaskController extends Controller { - public function newAction(Request $request) + public function new(Request $request) { $form = $this->createFormBuilder() ->add('task', TextType::class) @@ -681,8 +681,8 @@ option when building each field: .. code-block:: php-symfony - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/Controller/DefaultController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Validator\Constraints\NotBlank; @@ -692,7 +692,7 @@ option when building each field: class DefaultController extends Controller { - public function newAction(Request $request) + public function new(Request $request) { $form = $this->createFormBuilder() ->add('task', TextType::class, array( diff --git a/components/http_foundation.rst b/components/http_foundation.rst index fc50eed59d5..d0d8963581b 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -235,6 +235,12 @@ the method tells you if the request contains a session which was started in one of the previous requests. +.. versionadded:: 4.1 + Using :method:`Symfony\\Component\\HttpFoundation\\Request::getSession()` + when no session has been set was deprecated in Symfony 4.1. It will throw + an exception in Symfony 5.0 when the session is ``null``. Check for an existing session + first by calling :method:`Symfony\\Component\\HttpFoundation\\Request::hasSession()`. + Accessing ``Accept-*`` Headers Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -270,6 +276,19 @@ If you need to get full access to parsed data from ``Accept``, ``Accept-Language $acceptHeaders = AcceptHeader::fromString($request->headers->get('Accept')) ->all(); +The default values that can be optionally included in the ``Accept-*`` headers +are also supported:: + + $acceptHeader = 'text/plain;q=0.5, text/html, text/*;q=0.8, */*;q=0.3'; + $accept = AcceptHeader::fromString($acceptHeader); + + $quality = $accept->get('text/xml')->getQuality(); // $quality = 0.8 + $quality = $accept->get('application/xml')->getQuality(); // $quality = 0.3 + +.. versionadded:: 4.1 + The support of default values in the ``Accept-*`` headers was introduced in + Symfony 4.1. + Accessing other Data ~~~~~~~~~~~~~~~~~~~~ @@ -286,7 +305,7 @@ represents an HTTP message. But when moving from a legacy system, adding methods or changing some default behavior might help. In that case, register a PHP callable that is able to create an instance of your ``Request`` class:: - use AppBundle\Http\SpecialRequest; + use App\Http\SpecialRequest; use Symfony\Component\HttpFoundation\Request; Request::setFactory(function ( @@ -383,9 +402,6 @@ Note you can create a :class:`Symfony\\Component\\HttpFoundation\\Cookie` object from a raw header value using :method:`Symfony\\Component\\HttpFoundation\\Cookie::fromString`. -.. versionadded:: 3.3 - The ``Cookie::fromString()`` method was introduced in Symfony 3.3. - Managing the HTTP Cache ~~~~~~~~~~~~~~~~~~~~~~~ @@ -404,6 +420,13 @@ of methods to manipulate the HTTP headers related to the cache: * :method:`Symfony\\Component\\HttpFoundation\\Response::setEtag`; * :method:`Symfony\\Component\\HttpFoundation\\Response::setVary`; +.. note:: + + The methods :method:`Symfony\\Component\\HttpFoundation\\Response::setExpires`, + :method:`Symfony\\Component\\HttpFoundation\\Response::setLastModified` and + :method:`Symfony\\Component\\HttpFoundation\\Response::setDate` accept any + object that implements ``\DateTimeInterface``, including immutable date objects. + The :method:`Symfony\\Component\\HttpFoundation\\Response::setCache` method can be used to set the most commonly used cache information in one method call:: @@ -531,9 +554,6 @@ It is possible to delete the file after the request is sent with the :method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::deleteFileAfterSend` method. Please note that this will not work when the ``X-Sendfile`` header is set. -.. versionadded:: 3.3 - The ``Stream`` class was introduced in Symfony 3.3. - If the size of the served file is unknown (e.g. because it's being generated on the fly, or because a PHP stream filter is registered on it, etc.), you can pass a ``Stream`` instance to ``BinaryFileResponse``. This will disable ``Range`` and ``Content-Length`` @@ -585,10 +605,6 @@ class, which can make this even easier:: // if the data to send is already encoded in JSON $response = JsonResponse::fromJsonString('{ "data": 123 }'); -.. versionadded:: 3.2 - The :method:`Symfony\\Component\\HttpFoundation\\JsonResponse::fromJsonString` - method was added in Symfony 3.2. - The ``JsonResponse`` class sets the ``Content-Type`` header to ``application/json`` and encodes your data to JSON when needed. diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index ba7c95766ef..909ac440f22 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -72,8 +72,8 @@ The Symfony HttpFoundation component provides some by default and these can easily serve as examples if you wish to write your own. * :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler` -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcacheSessionHandler` * :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler` +* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\RedisSessionHandler` * :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler` * :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NullSessionHandler` @@ -148,7 +148,7 @@ configuration: .. code-block:: yaml - # config.yml + # config/packages/framework.yaml framework: session: gc_probability: null @@ -262,58 +262,6 @@ particular cookie by reading the ``getLifetime()`` method:: The expiry time of the cookie can be determined by adding the created timestamp and the lifetime. -PHP 5.4 Compatibility -~~~~~~~~~~~~~~~~~~~~~ - -Since PHP 5.4.0, :phpclass:`SessionHandler` and :phpclass:`SessionHandlerInterface` -are available. Symfony provides forward compatibility for the :phpclass:`SessionHandlerInterface` -so it can be used under PHP 5.3. This greatly improves interoperability with other -libraries. - -:phpclass:`SessionHandler` is a special PHP internal class which exposes native save -handlers to PHP user-space. - -In order to provide a solution for those using PHP 5.4, Symfony has a special -class called :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeSessionHandler` -which under PHP 5.4, extends from ``\SessionHandler`` and under PHP 5.3 is just a -empty base class. This provides some interesting opportunities to leverage -PHP 5.4 functionality if it is available. - -Save Handler Proxy -~~~~~~~~~~~~~~~~~~ - -A Save Handler Proxy is basically a wrapper around a Save Handler that was -introduced to seamlessly support the migration from PHP 5.3 to PHP 5.4+. It -further creates an extension point from where custom logic can be added that -works independently of which handler is being wrapped inside. - -There are two kinds of save handler class proxies which inherit from -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy`: -they are :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\NativeProxy` -and :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy`. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage` -automatically injects storage handlers into a save handler proxy unless already -wrapped by one. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\NativeProxy` -is used automatically under PHP 5.3 when internal PHP save handlers are specified -using the ``Native*SessionHandler`` classes, while -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy` -will be used to wrap any custom save handlers, that implement :phpclass:`SessionHandlerInterface`. - -From PHP 5.4 and above, all session handlers implement :phpclass:`SessionHandlerInterface` -including ``Native*SessionHandler`` classes which inherit from :phpclass:`SessionHandler`. - -The proxy mechanism allows you to get more deeply involved in session save handler -classes. A proxy for example could be used to encrypt any session transaction -without knowledge of the specific save handler. - -.. note:: - - Before PHP 5.4, you can only proxy user-land save handlers but not - native PHP save handlers. - .. _`php.net/session.customhandler`: https://php.net/session.customhandler .. _`php.net/session.configuration`: https://php.net/session.configuration .. _`php.net/memcached.setoption`: https://php.net/memcached.setoption diff --git a/components/http_kernel.rst b/components/http_kernel.rst index 8359500c90a..22fd33ffd6c 100644 --- a/components/http_kernel.rst +++ b/components/http_kernel.rst @@ -218,8 +218,8 @@ that implements :class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerRe and is one of the constructor arguments to ``HttpKernel``. Your job is to create a class that implements the interface and fill in its -two methods: ``getController()`` and ``getArguments()``. In fact, one default -implementation already exists, which you can use directly or learn from: +method: ``getController()``. In fact, one default implementation already +exists, which you can use directly or learn from: :class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver`. This implementation is explained more in the sidebar below:: @@ -230,30 +230,14 @@ This implementation is explained more in the sidebar below:: interface ControllerResolverInterface { public function getController(Request $request); - - public function getArguments(Request $request, $controller); } -.. caution:: - - The ``getArguments()`` method in the - :class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver` and - respective interface - :class:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface` - are deprecated as of 3.1 and will be removed in 4.0. You can use the - :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver` which - uses the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface` - instead. - Internally, the ``HttpKernel::handle()`` method first calls :method:`Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface::getController` on the controller resolver. This method is passed the ``Request`` and is responsible for somehow determining and returning a PHP callable (the controller) based on the request's information. -The second method, :method:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface::getArguments`, -will be called after another event - ``kernel.controller`` - is dispatched. - .. sidebar:: Resolving the Controller in the Symfony Framework The Symfony Framework uses the built-in @@ -269,11 +253,11 @@ will be called after another event - ``kernel.controller`` - is dispatched. information is typically placed on the ``Request`` via the ``RouterListener``). This string is then transformed into a PHP callable by doing the following: - a) The ``AcmeDemoBundle:Default:index`` format of the ``_controller`` key - is changed to another string that contains the full class and method - name of the controller by following the convention used in Symfony - e.g. - ``Acme\DemoBundle\Controller\DefaultController::indexAction``. This transformation - is specific to the :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver` + a) If the ``_controller`` key doesn't follow the recommended PHP namespace + format (e.g. ``App\Controller\DefaultController::index``) its format is + transformed into it. For example, the legacy ``FooBundle:Default:index`` + format would be changed to ``Acme\FooBundle\Controller\DefaultController::indexAction``. + This transformation is specific to the :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver` sub-class used by the Symfony Framework. b) A new instance of your controller class is instantiated with no @@ -310,11 +294,10 @@ on the event object that's passed to listeners on this event. the Symfony Framework, and many deal with collecting profiler data when the profiler is enabled. - One interesting listener comes from the `SensioFrameworkExtraBundle`_, - which is packaged with the Symfony Standard Edition. This listener's - `@ParamConverter`_ functionality allows you to pass a full object (e.g. a - ``Post`` object) to your controller instead of a scalar value (e.g. an - ``id`` parameter that was on your route). The listener - + One interesting listener comes from the `SensioFrameworkExtraBundle`_. This + listener's `@ParamConverter`_ functionality allows you to pass a full object + (e.g. a ``Post`` object) to your controller instead of a scalar value (e.g. + an ``id`` parameter that was on your route). The listener - ``ParamConverterListener`` - uses reflection to look at each of the arguments of the controller and tries to use different methods to convert those to objects, which are then stored in the ``attributes`` property of @@ -421,12 +404,11 @@ return a ``Response``. .. sidebar:: ``kernel.view`` in the Symfony Framework There is no default listener inside the Symfony Framework for the ``kernel.view`` - event. However, one core bundle - `SensioFrameworkExtraBundle`_ - *does* - add a listener to this event. If your controller returns an array, - and you place the `@Template`_ annotation above the controller, then this - listener renders a template, passes the array you returned from your - controller to that template, and creates a ``Response`` containing the - returned content from that template. + event. However, `SensioFrameworkExtraBundle`_ *does* add a listener to this + event. If your controller returns an array, and you place the `@Template`_ + annotation above the controller, then this listener renders a template, + passes the array you returned from your controller to that template, and + creates a ``Response`` containing the returned content from that template. Additionally, a popular community bundle `FOSRestBundle`_ implements a listener on this event which aims to give you a robust view layer @@ -512,9 +494,9 @@ as possible to the client (e.g. sending emails). .. sidebar:: ``kernel.terminate`` in the Symfony Framework - If you use the SwiftmailerBundle with Symfony and use ``memory`` spooling, - then the `EmailSenderListener`_ is activated, which actually delivers - any emails that you scheduled to send during the request. + If you use the :ref:`memory spooling ` option of the + default Symfony mailer, then the `EmailSenderListener`_ is activated, which + actually delivers any emails that you scheduled to send during the request. .. _component-http-kernel-kernel-exception: @@ -744,10 +726,10 @@ translation files, etc.) This overriding mechanism works because resources are referenced not by their physical path but by their logical path. For example, the ``services.xml`` file -stored in the ``Resources/config/`` directory of a bundle called AppBundle is -referenced as ``@AppBundle/Resources/config/services.xml``. This logical path +stored in the ``Resources/config/`` directory of a bundle called FooBundle is +referenced as ``@FooBundle/Resources/config/services.xml``. This logical path will work when the application overrides that file and even if you change the -directory of AppBundle. +directory of FooBundle. The HttpKernel component provides a method called :method:`Symfony\\Component\\HttpKernel\\Kernel::locateResource` which can be used to transform logical paths into physical paths:: @@ -756,7 +738,7 @@ which can be used to transform logical paths into physical paths:: // ... $kernel = new HttpKernel($dispatcher, $resolver); - $path = $kernel->locateResource('@AppBundle/Resources/config/services.xml'); + $path = $kernel->locateResource('@FooBundle/Resources/config/services.xml'); Learn more ---------- diff --git a/components/intl.rst b/components/intl.rst index ba21ad10141..75a5b15c12a 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -48,17 +48,6 @@ replace the intl classes: Composer automatically exposes these classes in the global namespace. -If you don't use Composer but the -:doc:`Symfony ClassLoader component `, -you need to expose them manually by adding the following lines to your autoload -code:: - - if (!function_exists('intl_is_failure')) { - require '/path/to/Icu/Resources/stubs/functions.php'; - - $loader->registerPrefixFallback('/path/to/Icu/Resources/stubs'); - } - Writing and Reading Resource Bundles ------------------------------------ diff --git a/components/ldap.rst b/components/ldap.rst index c3af9ed322a..b02cf9860c3 100644 --- a/components/ldap.rst +++ b/components/ldap.rst @@ -128,9 +128,17 @@ delete existing ones:: $result = $query->execute(); $entry = $result[0]; $entry->setAttribute('email', array('fabpot@symfony.com')); - $entryManager->update($entry); + $entityManager->update($entry); + + // Adding or removing values to a multi-valued attribute is more efficient than using update() + $entityManager->addAttributeValues($entry, 'telephoneNumber', array('+1.111.222.3333', '+1.222.333.4444')); + $entityManager->removeAttributeValues($entry, 'telephoneNumber', array('+1.111.222.3333', '+1.222.333.4444')); // Removing an existing entry $entryManager->remove(new Entry('cn=Test User,dc=symfony,dc=com')); +.. versionadded:: 4.1 + The ``addAttributeValues()`` and ``removeAttributeValues()`` methods + were introduced in Symfony 4.1. + .. _Packagist: https://packagist.org/packages/symfony/ldap diff --git a/components/lock.rst b/components/lock.rst index 36cbb0e1495..0d851dca8cc 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -8,9 +8,6 @@ The Lock Component The Lock Component creates and manages `locks`_, a mechanism to provide exclusive access to a shared resource. -.. versionadded:: 3.4 - The Lock component was introduced in Symfony 3.4. - Installation ------------ @@ -147,6 +144,23 @@ to reset the TTL to its original value:: $lock->release(); } +.. tip:: + + Another useful technique for long-running tasks is to pass a custom TTL as + an argument of the ``refresh()`` method to change the default lock TTL:: + + $lock = $factory->createLock('charts-generation', 30); + // ... + // refresh the lock for 30 seconds + $lock->refresh(); + // ... + // refresh the lock for 600 seconds (next refresh() call will be 30 seconds again) + $lock->refresh(600); + + .. versionadded:: 4.1 + The feature to pass a custom TTL as an argument of the ``refresh()`` + method was introduced in Symfony 4.1. + Available Stores ---------------- diff --git a/components/messenger.rst b/components/messenger.rst new file mode 100644 index 00000000000..55c0d0b2cc1 --- /dev/null +++ b/components/messenger.rst @@ -0,0 +1,193 @@ +.. index:: + single: Messenger + single: Components; Messenger + +The Messenger Component +======================= + + The Messenger component helps applications send and receive messages to/from + other applications or via message queues. + +Installation +------------ + +.. code-block:: terminal + + $ composer require symfony/messenger + +Alternatively, you can clone the ``_ repository. + +.. include:: /components/require_autoload.rst.inc + +Concepts +-------- + +.. image:: /_images/components/messenger/overview.png + +**Sender**: + Responsible for serializing and sending messages to *something*. This + something can be a message broker or a third party API for example. + +**Receiver**: + Responsible for deserializing and forwarding messages to handler(s). This + can be a message queue puller or an API endpoint for example. + +**Handler**: + Responsible for handling messages using the business logic applicable to the messages. + +Bus +--- + +The bus is used to dispatch messages. The behaviour of the bus is in its ordered +middleware stack. The component comes with a set of middleware that you can use. + +When using the message bus with Symfony's FrameworkBundle, the following middleware +are configured for you: + +#. ``LoggingMiddleware`` (logs the processing of your messages) +#. ``SendMessageMiddleware`` (enables asynchronous processing) +#. ``HandleMessageMiddleware`` (calls the registered handle) + +Example:: + + use App\Message\MyMessage; + use Symfony\Component\Messenger\MessageBus; + use Symfony\Component\Messenger\HandlerLocator; + use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; + + $bus = new MessageBus([ + new HandleMessageMiddleware(new HandlerLocator([ + MyMessage::class => $handler, + ])), + ]); + + $result = $bus->dispatch(new MyMessage(/* ... */)); + +.. note: + + Every middleware needs to implement the ``MiddlewareInterface`` interface. + +Handlers +-------- + +Once dispatched to the bus, messages will be handled by a "message handler". A +message handler is a PHP callable (i.e. a function or an instance of a class) +that will do the required processing for your message:: + + namespace App\MessageHandler; + + use App\Message\MyMessage; + + class MyMessageHandler + { + public function __invoke(MyMessage $message) + { + // Message processing... + } + } + +Adapters +-------- + +In order to send and receive messages, you will have to configure an adapter. An +adapter will be responsible of communicating with your message broker or 3rd parties. + +Your own sender +~~~~~~~~~~~~~~~ + +Using the ``SenderInterface``, you can easily create your own message sender. +Let's say you already have an ``ImportantAction`` message going through the +message bus and handled by a handler. Now, you also want to send this message as +an email. + +First, create your sender:: + + namespace App\MessageSender; + + use App\Message\ImportantAction; + use Symfony\Component\Message\SenderInterface; + + class ImportantActionToEmailSender implements SenderInterface + { + private $toEmail; + private $mailer; + + public function __construct(\Swift_Mailer $mailer, string $toEmail) + { + $this->mailer = $mailer; + $this->toEmail = $toEmail; + } + + public function send($message) + { + if (!$message instanceof ImportantAction) { + throw new \InvalidArgumentException(sprintf('Producer only supports "%s" messages.', ImportantAction::class)); + } + + $this->mailer->send( + (new \Swift_Message('Important action made')) + ->setTo($this->toEmail) + ->setBody( + '

Important action

Made by '.$message->getUsername().'

', + 'text/html' + ) + ); + } + } + +Your own receiver +~~~~~~~~~~~~~~~~~ + +A receiver is responsible for receiving messages from a source and dispatching +them to the application. + +Let's say you already processed some "orders" in your application using a +``NewOrder`` message. Now you want to integrate with a 3rd party or a legacy +application but you can't use an API and need to use a shared CSV file with new +orders. + +You will read this CSV file and dispatch a ``NewOrder`` message. All you need to +do is to write your custom CSV receiver and Symfony will do the rest. + +First, create your receiver:: + + namespace App\MessageReceiver; + + use App\Message\NewOrder; + use Symfony\Component\Message\ReceiverInterface; + use Symfony\Component\Serializer\SerializerInterface; + + class NewOrdersFromCsvFile implements ReceiverInterface + { + private $serializer; + private $filePath; + + public function __construct(SerializerInteface $serializer, string $filePath) + { + $this->serializer = $serializer; + $this->filePath = $filePath; + } + + public function receive(callable $handler) : void + { + $ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv'); + + foreach ($ordersFromCsv as $orderFromCsv) { + $handler(new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount'])); + } + } + + public function stop(): void + { + // noop + } + } + +Receiver and Sender on the same bus +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To allow us to receive and send messages on the same bus and prevent an infinite +loop, the message bus is equipped with the ``WrapIntoReceivedMessage`` middleware. +It will wrap the received messages into ``ReceivedMessage`` objects and the +``SendMessageMiddleware`` middleware will know it should not route these +messages again to an adapter. diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 846ff0d5c1c..27296716596 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -336,10 +336,6 @@ You may also pass fully qualified class or interface names (which is checked using ``instanceof``). Additionally, you can validate all items in an array recursively by suffixing the type with ``[]``. -.. versionadded:: 3.4 - Validating types of array items recursively was introduced in Symfony 3.4. - Prior to Symfony 3.4, only scalar values could be validated. - If you pass an invalid option now, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` is thrown:: diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index 2f6193ec7cf..5db1918b2a4 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -150,7 +150,7 @@ message, enclosed with ``/``. For example, with: - + @@ -430,10 +430,6 @@ namespaces in the ``phpunit.xml`` file, as done for example in the Modified PHPUnit script ----------------------- -.. versionadded:: 3.2 - This modified PHPUnit script was introduced in the 3.2 version of - this component. - This bridge provides a modified version of PHPUnit that you can call by using its ``bin/simple-phpunit`` command. It has the following features: @@ -451,6 +447,8 @@ The script writes the modified PHPUnit it builds in a directory that can be configured by the ``SYMFONY_PHPUNIT_DIR`` env var, or in the same directory as the ``simple-phpunit`` if it is not provided. +It's also possible to set this env var in the ``phpunit.xml.dist`` file. + If you have installed the bridge through Composer, you can run it by calling e.g.: .. code-block:: bash @@ -462,11 +460,15 @@ If you have installed the bridge through Composer, you can run it by calling e.g Set the ``SYMFONY_PHPUNIT_VERSION`` env var to e.g. ``5.5`` to change the base version of PHPUnit to ``5.5`` instead of the default ``5.3``. + It's also possible to set this env var in the ``phpunit.xml.dist`` file. + .. tip:: If you still need to use ``prophecy`` (but not ``symfony/yaml``), then set the ``SYMFONY_PHPUNIT_REMOVE`` env var to ``symfony/yaml``. + It's also possible to set this env var in the ``phpunit.xml.dist`` file. + Code coverage listener ---------------------- diff --git a/components/process.rst b/components/process.rst index b89ac4e5f55..913ceabb1aa 100644 --- a/components/process.rst +++ b/components/process.rst @@ -21,8 +21,11 @@ Alternatively, you can clone the ``_ reposit Usage ----- -The :class:`Symfony\\Component\\Process\\Process` class allows you to execute -a command in a sub-process:: +The :class:`Symfony\\Component\\Process\\Process` class executes a command in a +sub-process, taking care of the differences between operating system and +escaping arguments to prevent security issues. It replaces PHP functions like +:phpfunction:`exec`, :phpfunction:`passthru`, :phpfunction:`shell_exec` and +:phpfunction:`system`:: use Symfony\Component\Process\Process; use Symfony\Component\Process\Exception\ProcessFailedException; @@ -37,8 +40,18 @@ a command in a sub-process:: echo $process->getOutput(); -The component takes care of the subtle differences between the different platforms -when executing the command. +.. tip:: + + In addition to passing the command binary and its arguments as a string, you + can also pass them as an array, which is useful when building a complex + command programmatically:: + + // traditional string based commands + $builder = new Process('ls -lsa'); + // same example but using an array + $builder = new Process(array('ls', '-lsa')); + // the array can contain any number of arguments and options + $builder = new Process(array('ls', '-l', '-s', '-a')); The ``getOutput()`` method always returns the whole content of the standard output of the command and ``getErrorOutput()`` the content of the error @@ -79,9 +92,6 @@ for new output before going to the next iteration:: echo $data."\n"; } - .. versionadded:: 3.2 - The ``getIterator()`` method was introduced in Symfony 3.2. - The ``mustRun()`` method is identical to ``run()``, except that it will throw a :class:`Symfony\\Component\\Process\\Exception\\ProcessFailedException` if the process couldn't be executed successfully (i.e. the process exited @@ -285,38 +295,6 @@ instead:: ); $process->run(); -To make your code work better on all platforms, you might want to use the -:class:`Symfony\\Component\\Process\\ProcessBuilder` class instead:: - - use Symfony\Component\Process\ProcessBuilder; - - $processBuilder = new ProcessBuilder(array('ls', '-lsa')); - $processBuilder->getProcess()->run(); - -In case you are building a binary driver, you can use the -:method:`Symfony\\Component\\Process\\ProcessBuilder::setPrefix` method to prefix all -the generated process commands. - -The following example will generate two process commands for a tar binary -adapter:: - - use Symfony\Component\Process\ProcessBuilder; - - $processBuilder = new ProcessBuilder(); - $processBuilder->setPrefix('/usr/bin/tar'); - - // '/usr/bin/tar' '--list' '--file=archive.tar.gz' - echo $processBuilder - ->setArguments(array('--list', '--file=archive.tar.gz')) - ->getProcess() - ->getCommandLine(); - - // '/usr/bin/tar' '-xzf' 'archive.tar.gz' - echo $processBuilder - ->setArguments(array('-xzf', 'archive.tar.gz')) - ->getProcess() - ->getCommandLine(); - Process Timeout --------------- @@ -432,10 +410,6 @@ Use :method:`Symfony\\Component\\Process\\Process::disableOutput` and However, it is possible to pass a callback to the ``start``, ``run`` or ``mustRun`` methods to handle process output in a streaming fashion. - .. versionadded:: 3.1 - The ability to pass a callback to these methods when output is disabled - was added in Symfony 3.1. - Finding the Executable PHP Binary --------------------------------- @@ -449,6 +423,20 @@ absolute path of the executable PHP binary available on your server:: $phpBinaryPath = $phpBinaryFinder->find(); // $phpBinaryPath = '/usr/local/bin/php' (the result will be different on your computer) +Checking for TTY Support +------------------------ + +Another utility provided by this component is a method called +:method:`Symfony\\Component\\Process\\Process::isTtySupported` which returns +whether `TTY`_ is supported on the current operating system:: + + use Symfony\Component\Process\Process; + + $process = (new Process())->setTty(Process::isTtySupported()); + +.. versionadded:: 4.1 + The ``isTtySupported()`` method was introduced in Symfony 4.1. + .. _`Symfony Issue#5759`: https://github.com/symfony/symfony/issues/5759 .. _`PHP Bug#39992`: https://bugs.php.net/bug.php?id=39992 .. _`exec`: https://en.wikipedia.org/wiki/Exec_(operating_system) @@ -456,3 +444,4 @@ absolute path of the executable PHP binary available on your server:: .. _`PHP Documentation`: https://php.net/manual/en/pcntl.constants.php .. _Packagist: https://packagist.org/packages/symfony/process .. _`PHP streams`: http://www.php.net/manual/en/book.stream.php +.. _`TTY`: https://en.wikipedia.org/wiki/Tty_(unix) diff --git a/components/property_info.rst b/components/property_info.rst index 53d2a456110..0b240497dc5 100644 --- a/components/property_info.rst +++ b/components/property_info.rst @@ -236,45 +236,13 @@ provide whether properties are readable or writable as booleans. // Example Result: bool(false) The :class:`Symfony\\Component\\PropertyInfo\\Extractor\\ReflectionExtractor` looks -for getter/isser/setter method in addition to whether or not a property is public -to determine if it's accessible. +for getter/isser/setter/hasser method in addition to whether or not a property is public +to determine if it's accessible. This based on how the :doc:`PropertyAccess ` +works. -This is based on how :doc:`PropertyAccess ` works, -so it even looks for adder/remover methods and can transform between singular -and plural property names:: - - class SomeClass - { - private $analyses; - private $feet; - - public function addAnalyse(Dummy $analyse) - { - // ... - } - - public function removeAnalyse(Dummy $analyse) - { - // ... - } - - public function addFoot(Dummy $foot) - { - // ... - } - - public function removeFoot(Dummy $foot) - { - // ... - } - } - - // to be writable, both the adder and the remover methods must be defined - $propertyInfo->isWritable(SomeClass::class, 'analyses'); // returns true - $propertyInfo->isWritable(SomeClass::class, 'feet'); // returns true - -.. versionadded:: 3.2 - The support of adder/remover methods was introduced in Symfony 3.2. +.. versionadded:: 4.1 + The support of hasser methods in the ``ReflectionExtractor`` class was + introduced in Symfony 4.1. .. tip:: @@ -391,7 +359,7 @@ It can also provide return and scalar types for PHP 7+. .. code-block:: yaml - # app/config/config.yml + # config/packages/framework.yaml framework: property_info: enabled: true diff --git a/components/routing.rst b/components/routing.rst index b7d10117b9c..a11d9df46fe 100644 --- a/components/routing.rst +++ b/components/routing.rst @@ -247,7 +247,7 @@ If you're using the ``YamlFileLoader``, then route definitions look like this: .. code-block:: yaml - # routes.yml + # routes.yaml route1: path: /foo defaults: { _controller: 'MyController::fooAction' } @@ -257,7 +257,7 @@ If you're using the ``YamlFileLoader``, then route definitions look like this: defaults: { _controller: 'MyController::foobarAction' } To load this file, you can use the following code. This assumes that your -``routes.yml`` file is in the same directory as the below code:: +``routes.yaml`` file is in the same directory as the below code:: use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\YamlFileLoader; @@ -265,7 +265,7 @@ To load this file, you can use the following code. This assumes that your // looks inside *this* directory $fileLocator = new FileLocator(array(__DIR__)); $loader = new YamlFileLoader($fileLocator); - $routes = $loader->load('routes.yml'); + $routes = $loader->load('routes.yaml'); Besides :class:`Symfony\\Component\\Routing\\Loader\\YamlFileLoader` there are two other loaders that work the same way: @@ -340,7 +340,7 @@ automatically in the background if you want to use it. A basic example of the $router = new Router( new YamlFileLoader($fileLocator), - 'routes.yml', + 'routes.yaml', array('cache_dir' => __DIR__.'/cache'), $requestContext ); @@ -355,10 +355,6 @@ automatically in the background if you want to use it. A basic example of the Unicode Routing Support ~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.2 - UTF-8 support for route paths and requirements was introduced in - Symfony 3.2. - The Routing component supports UTF-8 characters in route paths and requirements. Thanks to the ``utf8`` route option, you can make Symfony match and generate routes with UTF-8 characters: @@ -367,8 +363,7 @@ routes with UTF-8 characters: .. code-block:: php-annotations - // src/AppBundle/Controller/DefaultController.php - namespace AppBundle\Controller; + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Routing\Annotation\Route; @@ -378,23 +373,21 @@ routes with UTF-8 characters: /** * @Route("/category/{name}", name="route1", options={"utf8": true}) */ - public function categoryAction() + public function category() { // ... } .. code-block:: yaml - # app/config/routing.yml route1: path: /category/{name} - defaults: { _controller: 'AppBundle:Default:category' } + defaults: { _controller: 'App\Controller\DefaultController::category' } options: utf8: true .. code-block:: xml - - AppBundle:Default:category + App\Controller\DefaultController::category .. code-block:: php - // app/config/routing.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('route1', new Route('/category/{name}', array( - '_controller' => 'AppBundle:Default:category', + '_controller' => 'App\Controller\DefaultController::category', ), array(), array( @@ -440,8 +432,7 @@ You can also include UTF-8 strings as routing requirements: .. code-block:: php-annotations - // src/AppBundle/Controller/DefaultController.php - namespace AppBundle\Controller; + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Routing\Annotation\Route; @@ -456,17 +447,16 @@ You can also include UTF-8 strings as routing requirements: * options={"utf8": true} * ) */ - public function defaultAction() + public function default() { // ... } .. code-block:: yaml - # app/config/routing.yml route2: path: /default/{default} - defaults: { _controller: 'AppBundle:Default:default' } + defaults: { _controller: 'App\Controller\DefaultController::default' } requirements: default: "한국어" options: @@ -474,7 +464,6 @@ You can also include UTF-8 strings as routing requirements: .. code-block:: xml - - AppBundle:Default:default + App\Controller\DefaultController::default 한국어 @@ -490,14 +479,13 @@ You can also include UTF-8 strings as routing requirements: .. code-block:: php - // app/config/routing.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('route2', new Route('/default/{default}', array( - '_controller' => 'AppBundle:Default:default', + '_controller' => 'App\Controller\DefaultController::default', ), array( 'default' => '한국어', @@ -519,13 +507,6 @@ You can also include UTF-8 strings as routing requirements: character in any language, ``\p{Greek}`` matches any Greek character, ``\P{Han}`` matches any character not included in the Chinese Han script. -.. note:: - - In Symfony 3.2, there is no need to explicitly set the ``utf8`` option. - As soon as Symfony finds a UTF-8 character in the route path or requirements, - it will automatically turn on the UTF-8 support. However, this behavior - is deprecated and setting the option will be required in Symfony 4.0. - Learn more ---------- diff --git a/components/security/firewall.rst b/components/security/firewall.rst index 04693441d18..431ee898b66 100644 --- a/components/security/firewall.rst +++ b/components/security/firewall.rst @@ -91,9 +91,6 @@ further than allowed. Firewall Config ~~~~~~~~~~~~~~~ -.. versionadded:: 3.2 - The ``FirewallConfig`` class was introduced in Symfony 3.2. - The information about a given firewall, such as its name, provider, context, entry point and access denied URL, is provided by instances of the :class:`Symfony\\Bundle\\SecurityBundle\\Security\\FirewallConfig` class. diff --git a/components/serializer.rst b/components/serializer.rst index 8929357f2df..df8f2d7bd55 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -159,10 +159,6 @@ needs three parameters: #. The name of the class this information will be decoded to #. The encoder used to convert that information into an array -.. versionadded:: 3.3 - Support for the ``allow_extra_attributes`` key in the context was introduced - in Symfony 3.3. - By default, additional attributes that are not mapped to the denormalized object will be ignored by the Serializer component. Set the ``allow_extra_attributes`` key of the deserialization context to ``false`` to let the serializer throw @@ -254,7 +250,7 @@ like the following:: // For XML // $classMetadataFactory = new ClassMetadataFactory(new XmlFileLoader('/path/to/your/definition.xml')); // For YAML - // $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yml')); + // $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yaml')); .. _component-serializer-attributes-groups-annotations: @@ -590,10 +586,6 @@ There are several types of normalizers available: Objects are normalized to a map of property names to property values. - .. versionadded:: 3.4 - The ability to handle parent classes for ``PropertyNormalizer`` was - introduced in Symfony 3.4. - :class:`Symfony\\Component\\Serializer\\Normalizer\\JsonSerializableNormalizer` This normalizer works with classes that implement :phpclass:`JsonSerializable`. @@ -612,10 +604,6 @@ There are several types of normalizers available: :phpclass:`DateTime` and :phpclass:`DateTimeImmutable`) into strings. By default it uses the RFC3339_ format. - .. versionadded:: 3.2 - Support for specifying datetime format during denormalization was - introduced in the ``DateTimeNormalizer`` in Symfony 3.2. - :class:`Symfony\\Component\\Serializer\\Normalizer\\DataUriNormalizer` This normalizer converts :phpclass:`SplFileInfo` objects into a data URI string (``data:...``) such that files can be embedded into serialized data. @@ -624,15 +612,30 @@ There are several types of normalizers available: This normalizer converts :phpclass:`DateInterval` objects into strings. By default it uses the ``P%yY%mM%dDT%hH%iM%sS`` format. - .. versionadded:: 3.4 - The ``DateIntervalNormalizer`` normalizer was added in Symfony 3.4. - .. _component-serializer-encoders: Encoders -------- -The Serializer component supports many formats out of the box: +Encoders turn **arrays** into **formats** and vice versa. They implement +:class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` +for encoding (array to format) and +:class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface` for decoding +(format to array). + +You can add new encoders to a Serializer instance by using its second constructor argument:: + + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Encoder\XmlEncoder; + use Symfony\Component\Serializer\Encoder\JsonEncoder; + + $encoders = array(new XmlEncoder(), new JsonEncoder()); + $serializer = new Serializer(array(), $encoders); + +Built-in Encoders +~~~~~~~~~~~~~~~~~ + +The Serializer component provides several built-in encoders: :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` This class encodes and decodes data in JSON_. @@ -650,8 +653,63 @@ The Serializer component supports many formats out of the box: All these encoders are enabled by default when using the Symfony Standard Edition with the serializer enabled. -.. versionadded:: 3.2 - The ``YamlEncoder`` and ``CsvEncoder`` encoders were introduced in Symfony 3.2 +The ``JsonEncoder`` +~~~~~~~~~~~~~~~~~~~ + +The ``JsonEncoder`` encodes to and decodes from JSON strings, based on the PHP +:phpfunction:`json_encode` and :phpfunction:`json_decode` functions. + +The ``CsvEncoder`` +~~~~~~~~~~~~~~~~~~~ + +The ``CsvEncoder`` encodes to and decodes from CSV. + +You can pass the context key ``as_collection`` in order to have the results +always as a collection. + +.. versionadded:: 4.1 + The ``as_collection`` option was introduced in Symfony 4.1. + +The ``XmlEncoder`` +~~~~~~~~~~~~~~~~~~ + +This encoder transforms arrays into XML and vice versa. + +For example, take an object normalized as following:: + + array('foo' => array(1, 2), 'bar' => true); + +The ``XmlEncoder`` will encode this object like that:: + + + + 1 + 2 + 1 + + +Be aware that this encoder will consider keys beginning with ``@`` as attributes:: + + $encoder = new XmlEncoder(); + $encoder->encode(array('foo' => array('@bar' => 'value'))); + // will return: + // + // + // + // + +You can pass the context key ``as_collection`` in order to have the results +always as a collection. + +.. versionadded:: 4.1 + The ``as_collection`` option was introduced in Symfony 4.1. + +The ``YamlEncoder`` +~~~~~~~~~~~~~~~~~~~ + +This encoder requires the :doc:`Yaml Component ` and +transforms from and to Yaml. + .. _component-serializer-handling-circular-references: @@ -841,6 +899,60 @@ because it is deeper than the configured maximum depth of 2:: ); */ +Instead of throwing an exception, a custom callable can be executed when the +maximum depth is reached. This is especially useful when serializing entities +having unique identifiers:: + + use Doctrine\Common\Annotations\AnnotationReader; + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Annotation\MaxDepth; + use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; + use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + + class Foo + { + public $id; + + /** + * @MaxDepth(1) + */ + public $child; + } + + $level1 = new Foo(); + $level1->id = 1; + + $level2 = new Foo(); + $level2->id = 2; + $level1->child = $level2; + + $level3 = new Foo(); + $level3->id = 3; + $level2->child = $level3; + + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $normalizer = new ObjectNormalizer($classMetadataFactory); + $normalizer->setMaxDepthHandler(function ($foo) { + return '/foos/'.$foo->id; + }); + + $serializer = new Serializer(array($normalizer)); + + $result = $serializer->normalize($level1, null, array(ObjectNormalizer::ENABLE_MAX_DEPTH => true)); + /* + $result = array( + 'id' => 1, + 'child' => array( + 'id' => 2, + 'child' => '/foos/3', + ), + ); + */ + +.. versionadded:: 4.1 + The ``setMaxDepthHandler()`` method was introduced in Symfony 4.1. + Handling Arrays --------------- @@ -1024,6 +1136,94 @@ will be thrown. The type enforcement of the properties can be disabled by settin the serializer context option ``ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT`` to ``true``. +Serializing Interfaces and Abstract Classes +------------------------------------------- + +When dealing with objects that are fairly similar or share properties, you may +use interfaces or abstract classes. The Serializer component allows you to +serialize and deserialize these objects using a *"discriminator class mapping"*. + +The discriminator is the field (in the serialized string) used to differentiate +between the possible objects. In practice, when using the Serializer component, +pass the :class:`Symfony\\Component\\Serializer\\Mapping\\ClassDiscriminatorResolver` +to the :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`. + +Consider an application that defines an abstract ``CodeRepository`` class +extended by ``GitHubCodeRepository`` and ``BitBucketCodeRepository`` classes. +This example shows how to serialize and deserialize those objects:: + + // ... + use Symfony\Component\Serializer\Encoder\JsonEncoder; + use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; + use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolver; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + use Symfony\Component\Serializer\Serializer; + + $discriminator = new ClassDiscriminatorResolver(); + $discriminator->addClassMapping(CodeRepository::class, new ClassDiscriminatorMapping('type', [ + 'github' => GitHubCodeRepository::class, + 'bitbucket' => BitBucketCodeRepository::class, + ])); + + $serializer = new Serializer( + array(new ObjectNormalizer(null, null, null, null, $discriminator)), + array('json' => new JsonEncoder()) + ); + + $serialized = $serializer->serialize(new GitHubCodeRepository()); + // {"type": "github"} + + $repository = $serializer->unserialize($serialized, CodeRepository::class, 'json'); + // instanceof GitHubCodeRepository + +If the class metadata factory is enabled as explained in the +:ref:`Attributes Groups section `, you +can use this simpler configuration: + +.. configuration-block:: + + .. code-block:: php-annotations + + namespace App; + + use Symfony\Component\Serializer\Annotation\DiscriminatorMap; + + /** + * @DiscriminatorMap(typeProperty="type", mapping={ + * "github"="App\GitHubCodeRepository", + * "bitbucket"="App\BitBucketCodeRepository" + * }) + */ + interface CodeRepository + { + // ... + } + + .. code-block:: yaml + + App\CodeRepository: + discriminator_map: + type_property: type + mapping: + github: 'App\GitHubCodeRepository' + bitbucket: 'App\BitBucketCodeRepository' + + .. code-block:: xml + + + + + + + + + + + Learn more ---------- diff --git a/components/stopwatch.rst b/components/stopwatch.rst index 9da437589c0..4500b49abad 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -50,8 +50,9 @@ while it is still running. $stopwatch = new Stopwatch(true); - .. versionadded:: 3.4 - Full precision support was introduced in Symfony 3.4. +The stopwatch can be reset to its original state at any given time with the +:method:`Symfony\\Component\\Stopwatch\\Stopwatch::reset` method, which deletes +all the data measured so far. You can also provide a category name to an event:: diff --git a/components/translation.rst b/components/translation.rst index c90e668551a..4581ebdc82f 100644 --- a/components/translation.rst +++ b/components/translation.rst @@ -126,7 +126,7 @@ file as the second argument, instead of an array:: // ... $translator->addLoader('yaml', new YamlFileLoader()); - $translator->addResource('yaml', 'path/to/messages.fr.yml', 'fr_FR'); + $translator->addResource('yaml', 'path/to/messages.fr.yaml', 'fr_FR'); The Translation Process ----------------------- diff --git a/components/translation/custom_formats.rst b/components/translation/custom_formats.rst index c466a526749..28694fd607f 100644 --- a/components/translation/custom_formats.rst +++ b/components/translation/custom_formats.rst @@ -119,7 +119,7 @@ YAML file are dumped into a text file with the custom format:: use Symfony\Component\Translation\Loader\YamlFileLoader; $loader = new YamlFileLoader(); - $translations = $loader->load(__DIR__ . '/translations/messages.fr_FR.yml' , 'fr_FR'); + $translations = $loader->load(__DIR__ . '/translations/messages.fr_FR.yaml' , 'fr_FR'); $dumper = new MyFormatDumper(); $dumper->dump($translations, array('path' => __DIR__.'/dumps')); diff --git a/components/translation/usage.rst b/components/translation/usage.rst index e287ccde705..a1cd0555f69 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -271,21 +271,6 @@ The second argument (``10`` in this example) is the *number* of objects being described and is used to determine which translation to use and also to populate the ``%count%`` placeholder. -.. versionadded:: 3.2 - - Before Symfony 3.2, the placeholder used to select the plural (``%count%`` - in this example) must be included in the third optional argument of the - ``transChoice()`` method:: - - $translator->transChoice( - 'There is one apple|There are %count% apples', - 10, - array('%count%' => 10) - ); - - Starting from Symfony 3.2, when the only placeholder is ``%count%``, you - don't have to pass this third argument. - Based on the given number, the translator chooses the right plural form. In English, most words have a singular form when there is exactly one object and a plural form for all other numbers (0, 2, 3...). So, if ``count`` is @@ -436,9 +421,6 @@ The ``$messages`` variable will have the following structure:: Adding Notes to Translation Contents ------------------------------------ -.. versionadded:: 3.4 - The feature to load and dump translation notes was introduced in Symfony 3.4. - Sometimes translators need additional context to better decide how to translate some content. This context can be provided with notes, which are a collection of comments used to store end user readable information. The only format that @@ -453,7 +435,7 @@ loaded/dumped when using this component inside a Symfony application: - + new true diff --git a/components/using_components.rst b/components/using_components.rst index dccdb144654..d076b4b725b 100644 --- a/components/using_components.rst +++ b/components/using_components.rst @@ -56,19 +56,6 @@ immediately:: // ... -Using all of the Components ---------------------------- - -If you want to use all of the Symfony Components, then instead of adding -them one by one, you can include the ``symfony/symfony`` package: - -.. code-block:: terminal - - $ composer require symfony/symfony - -This will also include the Bundle and Bridge libraries, which you may or -may not actually need. - Now what? --------- diff --git a/components/validator/resources.rst b/components/validator/resources.rst index 0c567636056..5310c762fe0 100644 --- a/components/validator/resources.rst +++ b/components/validator/resources.rst @@ -71,7 +71,7 @@ configure the locations of these files:: use Symfony\Component\Validator\Validation; $validator = Validation::createValidatorBuilder() - ->addYamlMapping('config/validation.yml') + ->addYamlMapping('config/validation.yaml') ->getValidator(); .. note:: diff --git a/components/var_dumper.rst b/components/var_dumper.rst index 020a8f13886..884dcfb76c8 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper.rst @@ -22,8 +22,8 @@ Alternatively, you can clone the ``_ repo .. note:: - If using it inside a Symfony application, make sure that the - DebugBundle is enabled in your ``app/AppKernel.php`` file. + If using it inside a Symfony application, make sure that the DebugBundle has + been installed (or run ``composer require debug`` to install it). .. _components-var-dumper-dump: @@ -85,9 +85,8 @@ current PHP SAPI: DebugBundle and Twig Integration -------------------------------- -The DebugBundle allows greater integration of the component into the Symfony -full-stack framework. It is enabled by default in the *dev* and *test* -environment of the Symfony Standard Edition. +The DebugBundle allows greater integration of this component into Symfony +applications. Since generating (even debug) output in the controller or in the model of your application may just break it by e.g. sending HTTP headers or @@ -113,9 +112,6 @@ option. Read more about this and other options in .. tip:: - .. versionadded:: 3.3 - The local search box was introduced in Symfony 3.3. - If the dumped contents are complex, consider using the local search box to look for specific variables or values. First, click anywhere on the dumped contents and then press :kbd:`Ctrl. + F` or :kbd:`Cmd. + F` to make the local @@ -134,7 +130,7 @@ This will provide you with two new assertions: :method:`Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait::assertDumpEquals` verifies that the dump of the variable given as the second argument matches - the expected dump provided as a string in the first argument. + the expected dump provided as the first argument. :method:`Symfony\\Component\\VarDumper\\Test\\VarDumperTestTrait::assertDumpMatchesFormat` is like the previous method but accepts placeholders in the expected dump, @@ -159,10 +155,19 @@ Example:: ] EOTXT; + // if the first argument is a string, it must be the whole expected dump $this->assertDumpEquals($expectedDump, $testedVar); + + // if the first argument is not a string, assertDumpEquals() dumps it + // and compares it with the dump of the second argument + $this->assertDumpEquals($testedVar, $testedVar); } } +.. versionadded:: 4.1 + The possibility of passing non-string variables as the first argument of + ``assertDumpEquals()`` was introduced in Symfony 4.1. + Dump Examples and Output ------------------------ diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index 325d4ce5756..49c88703089 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -59,9 +59,6 @@ you can configure these limits: items. Specifying ``-1`` removes the limit. :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMinDepth` - .. versionadded:: 3.4 - The ``setMinDepth()`` method was introduced in Symfony 3.4. - Configures the minimum tree depth where we are guaranteed to clone all the items. After this depth is reached, only ``setMaxItems`` items will be cloned. The default value is ``1``, which is consistent @@ -84,9 +81,6 @@ Before dumping it, you can further limit the resulting Removes internal objects' handles for sparser output (useful for tests). :method:`Symfony\\Component\\VarDumper\\Cloner\\Data::seek` - .. versionadded:: 3.2 - The ``seek()`` method was introduced in Symfony 3.2. - Selects only subparts of already cloned arrays, objects or resources. Unlike the previous limits on cloners that remove data on purpose, these can @@ -177,9 +171,6 @@ Another option for doing the same could be:: $output = $dumper->dump($cloner->cloneVar($variable), true); - .. versionadded:: 3.2 - The ability to return a string was introduced in Symfony 3.2. - Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` interface that specifies the :method:`dump(Data $data) ` @@ -205,10 +196,6 @@ method:: 'maxStringLength' => 160 )); -.. versionadded:: 3.2 - Support for passing display options to the ``dump()`` method was introduced - in Symfony 3.2. - The output format of a dumper can be fine tuned by the two flags ``DUMP_STRING_LENGTH`` and ``DUMP_LIGHT_ARRAY`` which are passed as a bitmap in the third constructor argument. They can also be set via environment @@ -220,14 +207,6 @@ The ``$filter`` argument of ``assertDumpEquals()`` can be used to pass a bit field of ``Caster::EXCLUDE_*`` constants and influences the expected output produced by the different casters. -.. versionadded:: 3.4 - The ``$filter`` argument of ``assertDumpEquals()`` was introduced in - Symfony 3.4. - -.. versionadded:: 3.1 - The ``DUMP_STRING_LENGTH`` and ``DUMP_LIGHT_ARRAY`` flags were introduced - in Symfony 3.1. - If ``DUMP_STRING_LENGTH`` is set, then the length of a string is displayed next to its content: @@ -378,11 +357,6 @@ properties not in the class declaration). Adding Semantics with Metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.2 - As of Symfony 3.2, casters can attach metadata attributes to - :class:`Symfony\\Component\\VarDumper\\Cloner\\Stub` objects to inform - dumpers about the precise type of the dumped values. - Since casters are hooked on specific classes or interfaces, they know about the objects they manipulate. By altering the ``$stub`` object (the third argument of any caster), one can transfer this knowledge to the resulting ``Data`` object, diff --git a/components/workflow.rst b/components/workflow.rst index 4fe9a6ccdc2..48c2312520a 100644 --- a/components/workflow.rst +++ b/components/workflow.rst @@ -8,9 +8,6 @@ The Workflow Component The Workflow component provides tools for managing a workflow or finite state machine. -.. versionadded:: 3.2 - The Workflow component was introduced in Symfony 3.2. - Installation ------------ @@ -57,11 +54,6 @@ these statuses are called **places**. You can define the workflow like this:: $marking = new SingleStateMarkingStore('currentState'); $workflow = new Workflow($definition, $marking); -.. versionadded:: 3.3 - The fluent interface for the ``DefinitionBuilder`` class was introduced in - Symfony 3.3. Before you had to call the ``addPlaces()``, ``addTransition()`` - and ``build()`` methods separately. - The ``Workflow`` can now help you to decide what actions are allowed on a blog post depending on what *place* it is in. This will keep your domain logic in one place and not spread all over your application. @@ -72,6 +64,7 @@ A registry will also help you to decide if a workflow supports the object you are trying to use it with:: use Symfony\Component\Workflow\Registry; + use Symfony\Component\Workflow\WorkflowInterface\InstanceOfSupportStrategy; use Acme\Entity\BlogPost; use Acme\Entity\Newsletter; @@ -79,8 +72,12 @@ are trying to use it with:: $newsletterWorkflow = ... $registry = new Registry(); - $registry->add($blogWorkflow, BlogPost::class); - $registry->add($newsletterWorkflow, Newsletter::class); + $registry->addWorkflow($blogWorkflow, new InstanceOfSupportStrategy(BlogPost::class)); + $registry->addWorkflow($newsletterWorkflow, new InstanceOfSupportStrategy(Newsletter::class)); + +.. versionadded:: 4.1 + The ``addWorkflow()`` method was introduced in Symfony 4.1. In previous + Symfony versions it was called ``add()``. Usage ----- diff --git a/components/yaml.rst b/components/yaml.rst index d02b7ee4be9..ec47ea6a9ee 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -125,10 +125,7 @@ contents of the given file path and converts them to a PHP value:: use Symfony\Component\Yaml\Yaml; - $value = Yaml::parseFile('/path/to/file.yml'); - -.. versionadded:: 3.4 - The ``parseFile()`` method was introduced in Symfony 3.4. + $value = Yaml::parseFile('/path/to/file.yaml'); If an error occurs during parsing, the parser throws a ``ParseException`` exception. @@ -149,7 +146,7 @@ array to its YAML representation:: $yaml = Yaml::dump($array); - file_put_contents('/path/to/file.yml', $yaml); + file_put_contents('/path/to/file.yaml', $yaml); If an error occurs during the dump, the parser throws a :class:`Symfony\\Component\\Yaml\\Exception\\DumpException` exception. @@ -209,10 +206,6 @@ changed using the third argument as follows:: Numeric Literals ................ -.. versionadded:: 3.2 - Support for parsing integers grouped by underscores was introduced in - Symfony 3.2. - Long numeric literals, being integer, float or hexadecimal, are known for their poor readability in code and configuration files. That's why YAML files allow to add underscores to improve their readability: @@ -263,9 +256,6 @@ representation of the object. Parsing and Dumping Objects as Maps ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.2 - Support for parsing and dumping objects as maps was introduced in Symfony 3.2. - You can dump objects as Yaml maps by using the ``DUMP_OBJECT_AS_MAP`` flag:: $object = new \stdClass(); @@ -353,9 +343,6 @@ syntax to parse them as proper PHP constants:: Parsing and Dumping of Binary Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.2 - Support for parsing and dumping binary data was introduced in Symfony 3.2. - You can dump binary data by using the ``DUMP_BASE64_BINARY_DATA`` flag:: $imageContents = file_get_contents(__DIR__.'/images/logo.png'); @@ -373,9 +360,6 @@ Binary data is automatically parsed if they include the ``!!binary`` YAML tag Parsing and Dumping Custom Tags ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.3 - Support for parsing and dumping custom tags was introduced in Symfony 3.3. - In addition to the built-in support of tags like ``!php/const`` and ``!!binary``, you can define your own custom YAML tags and parse them with the ``PARSE_CUSTOM_TAGS`` flag:: @@ -425,20 +409,20 @@ Then, execute the script for validating contents: .. code-block:: terminal # validates a single file - $ php lint.php path/to/file.yml + $ php lint.php path/to/file.yaml # or all the files in a directory $ php lint.php path/to/directory # or contents passed to STDIN - $ cat path/to/file.yml | php lint.php + $ cat path/to/file.yaml | php lint.php The result is written to STDOUT and uses a plain text format by default. Add the ``--format`` option to get the output in JSON format: .. code-block:: terminal - $ php lint.php path/to/file.yml --format json + $ php lint.php path/to/file.yaml --format json .. tip:: diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index 38cd86d29a5..2910f73075d 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -310,9 +310,6 @@ The YAML specification defines some tags to set the type of any data explicitly: Pz7Y6OjuDg4J+fn5OTk6enp 56enmleECcgggoBADs= -.. versionadded:: 3.4 - Support for the ``!!str`` tag was introduced in Symfony 3.4. - .. _YAML: http://yaml.org/ Unsupported YAML Features diff --git a/configuration.rst b/configuration.rst index e6a4358210a..88e96cb92e5 100644 --- a/configuration.rst +++ b/configuration.rst @@ -4,94 +4,75 @@ Configuring Symfony (and Environments) ====================================== -Every Symfony application consists of a collection of bundles that add useful tools -(:doc:`services `) to your project. Each bundle can be customized -via configuration files that live - by default - in the ``app/config`` directory. +Symfony applications can install third-party packages (bundles, libraries, etc.) +to bring in new features (:doc:`services `) to your project. +Each package can be customized via configuration files that live - by default - +in the ``config/`` directory. -Configuration: config.yml -------------------------- +Configuration: config/packages/ +------------------------------- -The main configuration file is called ``config.yml``: +The configuration for each package can be found in ``config/packages/``. For +instance, the framework bundle is configured in ``config/packages/framework.yaml``: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml - imports: - - { resource: parameters.yml } - - { resource: security.yml } - - { resource: services.yml } - + # config/packages/framework.yaml framework: - secret: '%secret%' - router: { resource: '%kernel.project_dir%/app/config/routing.yml' } - # ... - - # Twig Configuration - twig: - debug: '%kernel.debug%' - strict_variables: '%kernel.debug%' - - # ... + secret: '%env(APP_SECRET)%' + #default_locale: en + #csrf_protection: ~ + #http_method_override: true + #trusted_hosts: ~ + # https://symfony.com/doc/current/reference/configuration/framework.html#handler-id + #session: + # # The native PHP session handler will be used + # handler_id: ~ + #esi: ~ + #fragments: ~ + php_errors: + log: true .. code-block:: xml - - - - - - - - - - - - - - - - - - - - + + + + + + + .. code-block:: php - // app/config/config.php - $this->import('parameters.yml'); - $this->import('security.yml'); - $this->import('services.yml'); - - $container->loadFromExtension('framework', array( - 'secret' => '%secret%', - 'router' => array( - 'resource' => '%kernel.project_dir%/app/config/routing.php', - ), - // ... - )); - - // Twig Configuration - $container->loadFromExtension('twig', array( - 'debug' => '%kernel.debug%', - 'strict_variables' => '%kernel.debug%', - )); - - // ... - -Most top-level keys - like ``framework`` and ``twig`` - are configuration for a -specific bundle (i.e. ``FrameworkBundle`` and ``TwigBundle``). + # config/packages/framework.php + $container->loadFromExtension('framework', [ + 'secret' => '%env(APP_SECRET)%', + //'default_locale' => 'en', + //'csrf_protection' => null, + //'http_method_override' => true, + //'trusted_hosts' => null, + // https://symfony.com/doc/current/reference/configuration/framework.html#handler-id + //'session' => [ + // // The native PHP session handler will be used + // 'handler_id' => null, + //], + //'esi' => null, + //'fragments' => null, + 'php_errors' => [ + 'log' => true, + ], + ]); + +The top-level key (here ``framework``) references configuration for a specific +bundle (:doc:`FrameworkBundle ` in this case). .. sidebar:: Configuration Formats @@ -100,9 +81,7 @@ specific bundle (i.e. ``FrameworkBundle`` and ``TwigBundle``). choose whatever you like best. There is no performance difference: * :doc:`/components/yaml/yaml_format`: Simple, clean and readable; - * *XML*: More powerful than YAML at times & supports IDE autocompletion; - * *PHP*: Very powerful but less readable than standard configuration formats. Configuration Reference & Dumping @@ -111,15 +90,14 @@ Configuration Reference & Dumping There are *two* ways to know *what* keys you can configure: #. Use the :doc:`Reference Section `; - #. Use the ``config:dump-reference`` command. -For example, if you want to configure something in Twig, you can see an example -dump of all available configuration options by running: +For example, if you want to configure something related to the framework bundle, +you can see an example dump of all available configuration options by running: .. code-block:: terminal - $ php bin/console config:dump-reference twig + $ php bin/console config:dump-reference framework .. index:: single: Environments; Introduction @@ -127,127 +105,68 @@ dump of all available configuration options by running: .. _page-creation-environments: .. _page-creation-prod-cache-clear: -The imports Key: Loading other Configuration Files --------------------------------------------------- +.. _config-parameter-intro: + +The parameters Key: Parameters (Variables) +------------------------------------------ -Symfony's main configuration file is ``app/config/config.yml``. But, for organization, -it *also* loads other configuration files via its ``imports`` key: +The configuration has some special top-level keys. One of them is called +``parameters``: it's used to define *variables* that can be referenced in *any* +other configuration file. For example, when you install the *translation* +package, a ``locale`` parameter is added to ``config/services.yaml``: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml - imports: - - { resource: parameters.yml } - - { resource: security.yml } - - { resource: services.yml } + # config/services.yaml + parameters: + locale: en + # ... .. code-block:: xml - + + http://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/symfony + http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - - - - - + + en + .. code-block:: php - // app/config/config.php - $this->import('parameters.yml'); - $this->import('security.yml'); - $this->import('services.yml'); - + // config/services.php + $container->setParameter('locale', 'en'); // ... -The ``imports`` key works a lot like the PHP ``include()`` function: the contents of -``parameters.yml``, ``security.yml`` and ``services.yml`` are read and loaded. You -can also load XML files or PHP files. - -.. tip:: - - If your application uses unconventional file extensions (for example, your - YAML files have a ``.res`` extension) you can set the file type explicitly - with the ``type`` option: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - imports: - - { resource: parameters.res, type: yml } - # ... - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $this->import('parameters.res', 'yml'); - // ... - -.. _config-parameter-intro: - -The parameters Key: Parameters (Variables) ------------------------------------------- - -Another special key is called ``parameters``: it's used to define *variables* that -can be referenced in *any* other configuration file. For example, in ``config.yml``, -a ``locale`` parameter is defined and then referenced below under the ``framework`` -key: +This parameter is then referenced in the framework config in +``config/packages/translation.yaml``: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml - # ... - - parameters: - locale: en - + # config/packages/translation.yaml framework: - # ... - # any string surrounded by two % is replaced by that parameter value - default_locale: "%locale%" + default_locale: '%locale%' - # ... + # ... .. code-block:: xml - + - - - en - - + - - .. code-block:: php - // app/config/config.php - // ... - - $container->setParameter('locale', 'en'); - + // config/packages/translation.php $container->loadFromExtension('framework', array( + // any string surrounded by two % is replaced by that parameter value 'default_locale' => '%locale%', + // ... )); - // ... - You can define whatever parameter names you want under the ``parameters`` key of -any configuration file. To reference a parameter, surround its name with two percent -signs - e.g. ``%locale%``. +any configuration file. To reference a parameter, surround its name with two +percent signs - e.g. ``%locale%``. .. seealso:: @@ -295,110 +204,64 @@ signs - e.g. ``%locale%``. For more information about parameters - including how to reference them from inside a controller - see :ref:`service-container-parameters`. +.. _config-dot-env: .. _config-parameters-yml: -The Special parameters.yml File -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The .env File & Environment Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -On the surface, ``parameters.yml`` is just like any other configuration file: it -is imported by ``config.yml`` and defines several parameters: - -.. code-block:: yaml - - parameters: - # ... - database_user: root - database_password: ~ +There is also a ``.env`` file which is loaded. Its contents become environment +variables in the dev environment, making it easier to reference environment +variables in your code. When you install packages, more environment variables are +added to this file. But you can also add your own variables. -Not surprisingly, these are referenced from inside of ``config.yml`` and help to -configure DoctrineBundle and other parts of Symfony: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - driver: pdo_mysql - # ... - user: '%database_user%' - password: '%database_password%' - - .. code-block:: xml +Environment variables can be referenced in any other configuration files by using +a special syntax. For example, if you install the ``doctrine`` package, then you +will have an environment variable called ``DATABASE_URL`` in your ``.env`` file. +This is referenced inside ``config/packages/doctrine.yaml``: - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('doctrine', array( - 'dbal' => array( - 'driver' => 'pdo_mysql', - // ... - - 'user' => '%database_user%', - 'password' => '%database_password%', - ), - )); +.. code-block:: yaml -But the ``parameters.yml`` file *is* special: it defines the values that usually -change on each server. For example, the database credentials on your local -development machine might be different from your workmates. That's why this file -is not committed to the shared repository and is only stored on your machine. + # config/packages/doctrine.yaml + doctrine: + dbal: + url: '%env(DATABASE_URL)%' -Because of that, **parameters.yml is not committed to your version control**. In fact, -the ``.gitignore`` file that comes with Symfony prevents it from being committed. + # the resolve: prefix will resolve parameters *inside* the env variable + # url: '%env(resolve:DATABASE_URL)%' -However, a ``parameters.yml.dist`` file *is* committed (with dummy values). This file -isn't read by Symfony: it's just a reference so that Symfony knows which parameters -need to be defined in the ``parameters.yml`` file. If you add or remove keys to -``parameters.yml``, add or remove them from ``parameters.yml.dist`` too so both -files are always in sync. +For more details about environment variables, see :ref:`config-env-vars`. -.. sidebar:: The Interactive Parameter Handler +The ``.env`` file is special, because it defines the values that usually change +on each server. For example, the database credentials on your local development +machine might be different from your workmates. That's why this file is **not +committed to the shared repository** and is only stored on your machine. In +fact, the ``.gitignore`` file that comes with Symfony prevents it from being +committed. - When you :ref:`install an existing Symfony project `, you - will need to create the ``parameters.yml`` file using the committed ``parameters.yml.dist`` - file as a reference. To help with this, after you run ``composer install``, a - Symfony script will automatically create this file by interactively asking you - to supply the value for each parameter defined in ``parameters.yml.dist``. For - more details - or to remove or control this behavior - see the - `Incenteev Parameter Handler`_ documentation. +However, a ``.env.dist`` file *is* committed (with dummy values). This file +isn't read by Symfony: it's just a reference so that Symfony knows which +variables need to be defined in the ``.env`` file. If you add or remove keys to +``.env``, add or remove them from ``.env.dist`` too, so both files are always +in sync. Environments & the Other Config Files ------------------------------------- -You have just *one* app, but whether you realize it or not, you need it to behave -*differently* at different times: +You have just *one* app, but whether you realize it or not, you need it to +behave *differently* at different times: -* While **developing**, you want your app to log everything and expose nice debugging - tools; +* While **developing**, you want your app to log everything and expose nice + debugging tools; -* After deploying to **production**, you want that *same* app to be optimized for - speed and only log errors. +* After deploying to **production**, you want that *same* app to be optimized + for speed and only log errors. -How can you make *one* application behave in two different ways? With *environments*. +How can you make *one* application behave in two different ways? With +*environments*. -You've probably already been using the ``dev`` environment without even knowing it. -After you deploy, you'll use the ``prod`` environment. +You've probably already been using the ``dev`` environment without even knowing +it. After you deploy, you'll use the ``prod`` environment. To learn more about *how* to execute and control each environment, see :doc:`/configuration/environments`. diff --git a/configuration/configuration_organization.rst b/configuration/configuration_organization.rst index 5c22dfc2572..11f8c2aaede 100644 --- a/configuration/configuration_organization.rst +++ b/configuration/configuration_organization.rst @@ -4,205 +4,68 @@ How to Organize Configuration Files =================================== -The default Symfony Standard Edition defines three -:doc:`execution environments ` called -``dev``, ``prod`` and ``test``. An environment simply represents a way to -execute the same codebase with different configurations. +The Symfony skeleton defines three :doc:`execution environments ` +called ``dev``, ``prod`` and ``test``. An environment simply represents a way +to execute the same codebase with different configurations. In order to select the configuration file to load for each environment, Symfony -executes the ``registerContainerConfiguration()`` method of the ``AppKernel`` -class:: +executes the ``configureContainer()`` method of the ``Kernel`` class:: - // app/AppKernel.php - use Symfony\Component\HttpKernel\Kernel; + // src/Kernel.php use Symfony\Component\Config\Loader\LoaderInterface; + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; - class AppKernel extends Kernel + class Kernel extends BaseKernel { - // ... - - public function registerContainerConfiguration(LoaderInterface $loader) - { - $loader->load($this->getProjectDir().'/app/config/config_'.$this->getEnvironment().'.yml'); - } - } - -This method loads the ``app/config/config_dev.yml`` file for the ``dev`` -environment and so on. In turn, this file loads the common configuration file -located at ``app/config/config.yml``. Therefore, the configuration files of the -default Symfony Standard Edition follow this structure: - -.. code-block:: text - - your-project/ - ├─ app/ - │ ├─ ... - │ └─ config/ - │ ├─ config.yml - │ ├─ config_dev.yml - │ ├─ config_prod.yml - │ ├─ config_test.yml - │ ├─ parameters.yml - │ ├─ parameters.yml.dist - │ ├─ routing.yml - │ ├─ routing_dev.yml - │ └─ security.yml - ├─ ... - -This default structure was chosen for its simplicity — one file per environment. -But as any other Symfony feature, you can customize it to better suit your needs. -The following sections explain different ways to organize your configuration -files. In order to simplify the examples, only the ``dev`` and ``prod`` -environments are taken into account. + const CONFIG_EXTS = '.{php,xml,yaml,yml}'; -Different Directories per Environment -------------------------------------- - -Instead of suffixing the files with ``_dev`` and ``_prod``, this technique -groups all the related configuration files under a directory with the same -name as the environment: - -.. code-block:: text - - your-project/ - ├─ app/ - │ ├─ ... - │ └─ config/ - │ ├─ common/ - │ │ ├─ config.yml - │ │ ├─ parameters.yml - │ │ ├─ routing.yml - │ │ └─ security.yml - │ ├─ dev/ - │ │ ├─ config.yml - │ │ ├─ parameters.yml - │ │ ├─ routing.yml - │ │ └─ security.yml - │ └─ prod/ - │ ├─ config.yml - │ ├─ parameters.yml - │ ├─ routing.yml - │ └─ security.yml - ├─ ... - -To make this work, change the code of the -:method:`Symfony\\Component\\HttpKernel\\KernelInterface::registerContainerConfiguration` -method:: - - // app/AppKernel.php - use Symfony\Component\HttpKernel\Kernel; - use Symfony\Component\Config\Loader\LoaderInterface; - - class AppKernel extends Kernel - { // ... - public function registerContainerConfiguration(LoaderInterface $loader) + public function configureContainer(ContainerBuilder $container, LoaderInterface $loader) { - $loader->load($this->getProjectDir().'/app/config/'.$this->getEnvironment().'/config.yml'); + $confDir = $this->getProjectDir().'/config'; + $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob'); + if (is_dir($confDir.'/packages/'.$this->environment)) { + $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); + } + $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); + $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob'); } } -Then, make sure that each ``config.yml`` file loads the rest of the configuration -files, including the common files. For instance, this would be the imports -needed for the ``app/config/dev/config.yml`` file: +For the ``dev`` environment, Symfony loads the following config files and +directories and in this order: -.. configuration-block:: - - .. code-block:: yaml - - # app/config/dev/config.yml - imports: - - { resource: '../common/config.yml' } - - { resource: 'parameters.yml' } - - { resource: 'security.yml' } - - # ... - - .. code-block:: xml - - - - - - - - - - - - - +#. ``config/packages/*`` +#. ``config/packages/dev/*`` +#. ``config/services.yaml`` +#. ``config/services_dev.yaml`` - .. code-block:: php - - // app/config/dev/config.php - $loader->import('../common/config.php'); - $loader->import('parameters.php'); - $loader->import('security.php'); - - // ... - -.. include:: /components/dependency_injection/_imports-parameters-note.rst.inc - -Semantic Configuration Files ----------------------------- - -A different organization strategy may be needed for complex applications with -large configuration files. For instance, you could create one file per bundle -and several files to define all application services: +Therefore, the configuration files of the default Symfony applications follow +this structure: .. code-block:: text your-project/ - ├─ app/ - │ ├─ ... - │ └─ config/ - │ ├─ bundles/ - │ │ ├─ bundle1.yml - │ │ ├─ bundle2.yml - │ │ ├─ ... - │ │ └─ bundleN.yml - │ ├─ environments/ - │ │ ├─ common.yml - │ │ ├─ dev.yml - │ │ └─ prod.yml - │ ├─ routing/ - │ │ ├─ common.yml - │ │ ├─ dev.yml - │ │ └─ prod.yml - │ └─ services/ - │ ├─ frontend.yml - │ ├─ backend.yml - │ ├─ ... - │ └─ security.yml + ├─ config/ + │ └─ packages/ + │ ├─ dev/ + | │ ├─ framework.yaml + │ │ └─ ... + │ ├─ prod/ + │ │ └─ ... + │ ├─ test/ + │ │ └─ ... + | ├─ framework.yaml + │ └─ ... + │ ├─ services.yaml + │ └─ services_dev.yaml ├─ ... -Again, change the code of the ``registerContainerConfiguration()`` method to -make Symfony aware of the new file organization:: - - // app/AppKernel.php - use Symfony\Component\HttpKernel\Kernel; - use Symfony\Component\Config\Loader\LoaderInterface; - - class AppKernel extends Kernel - { - // ... - - public function registerContainerConfiguration(LoaderInterface $loader) - { - $loader->load($this->getProjectDir().'/app/config/environments/'.$this->getEnvironment().'.yml'); - } - } - -Following the same technique explained in the previous section, make sure to -import the appropriate configuration files from each main file (``common.yml``, -``dev.yml`` and ``prod.yml``). +This default structure was chosen for its simplicity — one file per package and +environment. But as any other Symfony feature, you can customize it to better +suit your needs. Advanced Techniques ------------------- @@ -215,24 +78,22 @@ Mix and Match Configuration Formats ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configuration files can import files defined with any other built-in configuration -format (``.yml``, ``.xml``, ``.php``, ``.ini``): +format (``.yaml`` or ``.yml``, ``.xml``, ``.php``, ``.ini``): .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml imports: - - { resource: 'parameters.yml' } - - { resource: 'services.xml' } - - { resource: 'security.yml' } + - { resource: 'my_config_file.xml' } - { resource: 'legacy.php' } # ... .. code-block:: xml - + - - - + @@ -253,21 +112,12 @@ format (``.yml``, ``.xml``, ``.php``, ``.ini``): .. code-block:: php - // app/config/config.php - $loader->import('parameters.yml'); - $loader->import('services.xml'); - $loader->import('security.yml'); - $loader->import('legacy.php'); + // config/services.php + $loader->import('my_config_file.yaml'); + $loader->import('legacy.xml'); // ... -.. caution:: - - The ``IniFileLoader`` parses the file contents using the - :phpfunction:`parse_ini_file` function. Therefore, you can only set - parameters to string values. Use one of the other loaders if you want - to use other data types (e.g. boolean, integer, etc.). - If you use any other configuration format, you have to define your own loader class extending it from :class:`Symfony\\Component\\DependencyInjection\\Loader\\FileLoader`. When the configuration values are dynamic, you can use the PHP configuration @@ -279,7 +129,7 @@ Global Configuration Files Some system administrators may prefer to store sensitive parameters in files outside the project directory. Imagine that the database credentials for your -website are stored in the ``/etc/sites/mysite.com/parameters.yml`` file. Loading +website are stored in the ``/etc/sites/mysite.com/parameters.yaml`` file. Loading this file is as simple as indicating the full file path when importing it from any other configuration file: @@ -287,16 +137,15 @@ any other configuration file: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml imports: - - { resource: 'parameters.yml' } - - { resource: '/etc/sites/mysite.com/parameters.yml' } + - { resource: '/etc/sites/mysite.com/parameters.yaml', ignore_errors: true } # ... .. code-block:: xml - + - - + @@ -315,56 +163,18 @@ any other configuration file: .. code-block:: php - // app/config/config.php - $loader->import('parameters.yml'); - $loader->import('/etc/sites/mysite.com/parameters.yml'); + // config/services.php + $loader->import('/etc/sites/mysite.com/parameters.yaml', null, true); // ... -Most of the time, local developers won't have the same files that exist on the -production servers. For that reason, the Config component provides the -``ignore_errors`` option to silently discard errors when the loaded file -doesn't exist: - -.. configuration-block:: - - .. code-block:: yaml +.. tip:: - # app/config/config.yml - imports: - - { resource: 'parameters.yml' } - - { resource: '/etc/sites/mysite.com/parameters.yml', ignore_errors: true } - - # ... - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $loader->import('parameters.yml'); - $loader->import('/etc/sites/mysite.com/parameters.yml', null, true); - - // ... + The ``ignore_errors`` option (which is the third optional argument in the + loader's ``import()`` method) silently discards errors when the loaded file + doesn't exist. This is needed in this case because most of the time, local + developers won't have the same files that exist on the production servers. As you've seen, there are lots of ways to organize your configuration files. You can choose one of these or even create your own custom way of organizing the -files. Don't feel limited by the Standard Edition that comes with Symfony. For even -more customization, see ":doc:`/configuration/override_dir_structure`". +files. For even more customization, see ":doc:`/configuration/override_dir_structure`". diff --git a/configuration/environments.rst b/configuration/environments.rst index abc5ab13e33..de67c3fec45 100644 --- a/configuration/environments.rst +++ b/configuration/environments.rst @@ -20,125 +20,58 @@ Different Environments, different Configuration Files ----------------------------------------------------- A typical Symfony application begins with three environments: ``dev``, -``prod``, and ``test``. As mentioned, each environment simply represents -a way to execute the same codebase with different configuration. It should -be no surprise then that each environment loads its own individual configuration -file. If you're using the YAML configuration format, the following files -are used: +``prod`` and ``test``. As mentioned, each environment represents a way to +execute the same codebase with different configuration. It should be no +surprise then that each environment loads its own individual configuration +files. These different files are organized by environment: -* for the ``dev`` environment: ``app/config/config_dev.yml`` -* for the ``prod`` environment: ``app/config/config_prod.yml`` -* for the ``test`` environment: ``app/config/config_test.yml`` +* for the ``dev`` environment: ``config/packages/dev/`` +* for the ``prod`` environment: ``config/packages/prod/`` +* for the ``test`` environment: ``config/packages/test/`` -This works via a simple standard that's used by default inside the ``AppKernel`` -class:: +In reality, each environment differs only somewhat from others. This means that +all environments share a large base of common configurations. This configuration +is put in files directly in the ``config/packages/`` directory. - // app/AppKernel.php +The location of these files is defined by the application's kernel:: - // ... + // src/Kernel.php - class AppKernel extends Kernel + // ... + class Kernel extends BaseKernel { // ... - public function registerContainerConfiguration(LoaderInterface $loader) + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) { - $loader->load($this->getProjectDir().'/app/config/config_'.$this->getEnvironment().'.yml'); - } - } - -As you can see, when Symfony is loaded, it uses the given environment to -determine which configuration file to load. This accomplishes the goal of -multiple environments in an elegant, powerful and transparent way. - -Of course, in reality, each environment differs only somewhat from others. -Generally, all environments will share a large base of common configuration. -Opening the ``config_dev.yml`` configuration file, you can see how this is -accomplished easily and transparently: - -.. configuration-block:: - - .. code-block:: yaml - - imports: - - { resource: config.yml } - - # ... - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - $loader->import('config.php'); - - // ... - -To share common configuration, each environment's configuration file -simply first imports from a central configuration file (``config.yml``). -The remainder of the file can then deviate from the default configuration -by overriding individual parameters. For example, by default, the ``web_profiler`` -toolbar is disabled. However, in the ``dev`` environment, the toolbar is -activated by modifying the value of the ``toolbar`` option in the ``config_dev.yml`` -configuration file: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config_dev.yml - imports: - - { resource: config.yml } - - web_profiler: - toolbar: true - # ... - - .. code-block:: xml - - - - - - - - + // ... + $confDir = $this->getProjectDir().'/config'; - + // always load all files in /config/packages/ + $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob'); - + // then, if available, load the files in the specific environment directory + if (is_dir($confDir.'/packages/'.$this->environment)) { + $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); + } - .. code-block:: php + // load a special services.(yaml/xml/php) and, if available, services_ENVIRONMENT.(yaml/xml/php) file + $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); + $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob'); + } + } - // app/config/config_dev.php - $loader->import('config.php'); +Take the framework package, installed by default, as an example: - $container->loadFromExtension('web_profiler', array( - 'toolbar' => true, - // ... - )); +* Loaded in all environments, ``config/packages/framework.yaml`` configures the + framework with some ``secret`` setting; +* In the **prod** environment, nothing extra will be set as there is no + ``config/packages/prod/`` directory; +* The same applies to **dev**, as there is no + ``config/packages/dev/framework.yaml``. There are however other packages (e.g. + ``routing.yaml``) with special dev settings; +* At last, during the **test** environment, the framework's test features are + enabled in ``config/packages/test/framework.yaml``. .. index:: single: Environments; Executing different environments @@ -146,77 +79,77 @@ configuration file: Executing an Application in different Environments -------------------------------------------------- -To execute the application in each environment, load up the application using -either ``app.php`` (for the ``prod`` environment) or ``app_dev.php`` -(for the ``dev`` environment) front controller: +To execute the application in each environment, change the ``APP_ENV`` +environment variable. During development, this is done in ``.env``: -.. code-block:: text +.. code-block:: bash + + # .env + APP_ENV=dev - http://localhost/app.php -> *prod* environment - http://localhost/app_dev.php -> *dev* environment + # or for test: + #APP_ENV=test -If you don't have *either* filename in your URL, then it's up to your web server -to decide *which* file to execute behind the scenes. If you're using the built-in -PHP web server, it knows to use the ``app_dev.php`` file. On production, you'll -:doc:`configure your web server ` to use ``app.php``. -Either way: *one of these two files is always executed*. +Visit the ``http://localhost:8000/index.php`` page in your web browser to see +your application in the configured environment. + +.. tip:: + + In production, it is recommended to configure the environment variables in + your :ref:`web server configuration `. .. note:: - The given URLs assume that your web server is configured to use the ``web/`` - directory of the application as its root. Read more in - :doc:`Installing Symfony `. + The given URLs assume that your web server is configured to use the ``public/`` + directory of the application as its root. Read more in :doc:`Installing Symfony `. -If you open up one of these files, you'll quickly see that the environment -used by each is explicitly set:: +If you open the file you just visited (``public/index.php``), you'll see that +the environment variable is passed to the kernel:: - // web/app.php - // ... + // public/index.php - $kernel = new AppKernel('prod', false); + // ... + $kernel = new Kernel($_SERVER['APP_ENV'] ?? 'dev', $_SERVER['APP_DEBUG'] ?? false); // ... -The ``prod`` key specifies that this application will run in the ``prod`` -environment. A Symfony application can be executed in any environment by using -this code and changing the environment string. +You can also replace ``$_SERVER['APP_ENV'] ?? 'dev'`` by just ``'dev'`` to +always run the application in the dev environment, independent of the +``APP_ENV`` variable. .. note:: The ``test`` environment is used when writing functional tests and is - not accessible in the browser directly via a front controller. In other - words, unlike the other environments, there is no ``app_test.php`` front - controller file. + usually not accessed in the browser directly via a front controller. .. index:: single: Configuration; Debug mode .. sidebar:: *Debug* Mode - Important, but unrelated to the topic of *environments* is the ``false`` - argument as the second argument to the ``AppKernel`` constructor. This - specifies if the application should run in "debug mode". Regardless - of the environment, a Symfony application can be run with debug mode - set to ``true`` or ``false``. This affects many things in the application, - such as displaying stacktraces on error pages or if cache files are - dynamically rebuilt on each request. Though not a requirement, debug mode - is generally set to ``true`` for the ``dev`` and ``test`` environments and - ``false`` for the ``prod`` environment. + Important, but unrelated to the topic of *environments* is the second + argument to the ``Kernel`` constructor. This specifies if the application + should run in "debug mode". Regardless of the environment, a Symfony + application can be run with debug mode set to ``true`` or ``false`` + (respectively ``1`` or ``0`` for the ``APP_DEBUG`` variable defined in + ``.env``). This affects many things in the application, such as displaying + stacktraces on error pages or if cache files are dynamically rebuilt on + each request. Though not a requirement, debug mode is generally set to + ``true`` for the ``dev`` and ``test`` environments and ``false`` for the + ``prod`` environment. Internally, the value of the debug mode becomes the ``kernel.debug`` parameter used inside the :doc:`service container `. If you look inside the application configuration file, you'll see the - parameter used, for example, to turn logging on or off when using the - Doctrine DBAL: + parameter used, for example, to turn Twig's debug mode on: .. configuration-block:: .. code-block:: yaml - doctrine: - dbal: - logging: '%kernel.debug%' - # ... + # config/packages/twig.yaml + twig: + debug: '%kernel.debug%' .. code-block:: xml @@ -226,29 +159,27 @@ this code and changing the environment string. xmlns:doctrine="http://symfony.com/schema/dic/doctrine" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/doctrine - http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd"> + http://symfony.com/schema/dic/twig + http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - + .. code-block:: php - $container->loadFromExtension('doctrine', array( - 'dbal' => array( - 'logging' => '%kernel.debug%', - // ... - ), + $container->loadFromExtension('twig', array( + 'debug' => '%kernel.debug%', // ... )); Selecting the Environment for Console Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -By default, Symfony commands are executed in the ``dev`` environment and with the -debug mode enabled. Use the ``--env`` and ``--no-debug`` options to modify this -behavior: +By default, Symfony commands are executed in whatever environment is defined by +the ``APP_ENV`` environment variable (usually configured in your ``.env`` file). + +Use the ``--env`` and ``--no-debug`` options to modify this behavior: .. code-block:: terminal @@ -261,31 +192,14 @@ behavior: # 'test' environment and debug disabled $ php bin/console command_name --env=test --no-debug -In addition to the ``--env`` and ``--no-debug`` options, the behavior of Symfony -commands can also be controlled with environment variables. The Symfony console -application checks the existence and value of these environment variables before -executing any command: - -``SYMFONY_ENV`` - Sets the execution environment of the command to the value of this variable - (``dev``, ``prod``, ``test``, etc.); -``SYMFONY_DEBUG`` - If ``0``, debug mode is disabled. Otherwise, debug mode is enabled. - -These environment variables are very useful for production servers because they -allow you to ensure that commands always run in the ``prod`` environment without -having to add any command option. - .. index:: single: Environments; Creating a new environment Creating a new Environment -------------------------- -By default, a Symfony application has three environments that handle most -cases. Of course, since an environment is nothing more than a string that -corresponds to a set of configuration, creating a new environment is quite -easy. +Since an environment is nothing more than a string that corresponds to a set of +configuration, creating a new environment is quite easy. Suppose, for example, that before deployment, you need to benchmark your application. One way to benchmark the application is to use near-production @@ -293,22 +207,20 @@ settings, but with Symfony's ``web_profiler`` enabled. This allows Symfony to record information about your application while benchmarking. The best way to accomplish this is via a new environment called, for example, -``benchmark``. Start by creating a new configuration file: +``benchmark``. Start by creating a new configuration directory and a +configuration file: .. configuration-block:: .. code-block:: yaml - # app/config/config_benchmark.yml - imports: - - { resource: config_prod.yml } - + # config/packages/benchmark/web_profiler.yaml framework: profiler: { only_exceptions: false } .. code-block:: xml - + - - - - @@ -330,50 +238,77 @@ The best way to accomplish this is via a new environment called, for example, .. code-block:: php - // app/config/config_benchmark.php - $loader->import('config_prod.php'); - + // config/packages/benchmark/web_profiler.php $container->loadFromExtension('framework', array( 'profiler' => array('only_exceptions' => false), )); -.. include:: /components/dependency_injection/_imports-parameters-note.rst.inc +And... you're finished! The application now supports a new environment called +``benchmark``. -And with this simple addition, the application now supports a new environment -called ``benchmark``. +Change the ``APP_ENV`` variable to ``benchmark`` to be able to access the new +environment through your browser: -This new configuration file imports the configuration from the ``prod`` environment -and modifies it. This guarantees that the new environment is identical to -the ``prod`` environment, except for any changes explicitly made here. +.. code-block:: bash -Because you'll want this environment to be accessible via a browser, you -should also create a front controller for it. Copy the ``web/app.php`` file -to ``web/app_benchmark.php`` and edit the environment to be ``benchmark``:: + # .env + APP_ENV=benchmark - // web/app_benchmark.php - // ... +.. sidebar:: Importing configuration - // change just this line - $kernel = new AppKernel('benchmark', false); + Besides loading files in the Kernel, you can also import files in the + configuration directly. For instance, to make sure the benchmark + environment is identical to the prod environment, you might want to load + all its configuration as well. - // ... + You can achieve this by using a special ``imports`` key: -The new environment is now accessible via:: + .. configuration-block: - http://localhost/app_benchmark.php + .. code-block:: yaml -.. note:: + # config/packages/benchmark/other.yaml + imports: + - { resource: '../prod/' } - Some environments, like the ``dev`` environment, are never meant to be - accessed on any deployed server by the public. This is because - certain environments, for debugging purposes, may give too much information - about the application or underlying infrastructure. To be sure these environments - aren't accessible, the front controller is usually protected from external - IP addresses via the following code at the top of the controller:: + # other resources are possible as well, like importing other + # files or using globs: + #- { resource: '/etc/myapp/some_special_config.xml' } + #- { resource: '/etc/myapp/*.yaml' } - if (!in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) { - die('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.'); - } + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/benchmark/other.php + $loader->import('../prod/'); + + // other resources are possible as well, like importing other + // files or using globs: + //$loader->import('/etc/myapp/some_special_config.yaml'); + //$loader->import('/etc/myapp/*.php'); .. index:: single: Environments; Cache directory @@ -385,7 +320,7 @@ Symfony takes advantage of caching in many ways: the application configuration, routing configuration, Twig templates and more are cached to PHP objects stored in files on the filesystem. -By default, these cached files are largely stored in the ``var/cache`` directory. +By default, these cached files are largely stored in the ``var/cache/`` directory. However, each environment caches its own set of files: .. code-block:: text @@ -399,8 +334,8 @@ However, each environment caches its own set of files: Sometimes, when debugging, it may be helpful to inspect a cached file to understand how something is working. When doing so, remember to look in -the directory of the environment you're using (most commonly ``dev`` while -developing and debugging). While it can vary, the ``var/cache/dev`` directory +the directory of the environment you're using (most commonly ``dev/`` while +developing and debugging). While it can vary, the ``var/cache/dev/`` directory includes the following: ``appDevDebugProjectContainer.php`` diff --git a/configuration/external_parameters.rst b/configuration/external_parameters.rst index 7862eabf153..375360985d3 100644 --- a/configuration/external_parameters.rst +++ b/configuration/external_parameters.rst @@ -4,39 +4,46 @@ How to Set external Parameters in the Service Container ======================================================= -In the article :doc:`/configuration`, you learned how to manage your application +In :doc:`/configuration`, you learned how to manage your application configuration. At times, it may benefit your application to store certain credentials outside of your project code. Database configuration is one such example. The flexibility of the Symfony service container allows you to easily do this. +.. _config-env-vars: + Environment Variables --------------------- -.. versionadded:: 3.2 - ``env()`` parameters were introduced in Symfony 3.2. - You can reference environment variables by using special parameters named after the variables you want to use enclosed between ``env()``. Their actual values will be resolved at runtime (once per request), so that dumped containers can be reconfigured dynamically even after being compiled. -For example, if you want to use the value of the ``DATABASE_HOST`` environment -variable in your service container configuration, you can reference it using -``%env(DATABASE_HOST)%`` in your configuration files: +For example, when installing the ``doctrine`` recipe, database configuration is +put in a ``DATABASE_URL`` environment variable: + +.. code-block:: bash + + # .env + DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" + +This variable is referenced in the service container configuration using +``%env(DATABASE_URL)%``: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/packages/doctrine.yaml doctrine: dbal: - host: '%env(DATABASE_HOST)%' + url: '%env(DATABASE_URL)%' + # ... .. code-block:: xml - + @@ -56,10 +63,10 @@ variable in your service container configuration, you can reference it using .. code-block:: php - // app/config/config.php + // config/packages/doctrine.php $container->loadFromExtension('doctrine', array( 'dbal' => array( - 'host' => '%env(DATABASE_HOST)%', + 'url' => '%env(DATABASE_URL)%', ) )); @@ -70,34 +77,37 @@ will be used whenever the corresponding environment variable is *not* found: .. code-block:: yaml - # app/config/parameters.yml + # config/services.yaml parameters: - database_host: '%env(DATABASE_HOST)%' env(DATABASE_HOST): localhost .. code-block:: xml - + - %env(DATABASE_HOST)% localhost .. code-block:: php - // app/config/parameters.php - $container->setParameter('database_host', '%env(DATABASE_HOST)%'); + // config/services.php $container->setParameter('env(DATABASE_HOST)', 'localhost'); -Setting environment variables is generally done at the web server level or in the -terminal. If you're using Apache, Nginx or just the console, you can use e.g. one -of the following: +.. _configuration-env-var-in-prod: + +Configuring Environment Variables in Production +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +During development, you'll use the ``.env`` file to configure your environment +variables. On your production server, it is recommended to configure these at +the web server level. If you're using Apache or Nginx, you can use e.g. one of +the following: .. configuration-block:: @@ -106,34 +116,19 @@ of the following: # ... - SetEnv DATABASE_USER user - SetEnv DATABASE_PASSWORD secret + SetEnv DATABASE_URL "mysql://db_user:db_password@127.0.0.1:3306/db_name" .. code-block:: nginx - fastcgi_param DATABASE_USER user; - fastcgi_param DATABASE_PASSWORD secret; + fastcgi_param DATABASE_URL "mysql://db_user:db_password@127.0.0.1:3306/db_name"; - .. code-block:: terminal +.. caution:: - $ export DATABASE_USER=user - $ export DATABASE_PASSWORD=secret - -.. tip:: - - .. versionadded:: 3.3 - The support of the special ``SYMFONY__`` environment variables was - deprecated in Symfony 3.3 and it will be removed in 4.0. Instead of - using those variables, define regular environment variables and get - their values using the ``%env(...)%`` syntax in your config files. - - You can also define the default value of any existing parameters using - special environment variables named after their corresponding parameter - prefixed with ``SYMFONY__`` after replacing dots by double underscores - (e.g. ``SYMFONY__KERNEL__CHARSET`` to set the default value of the - ``kernel.charset`` parameter). These default values are resolved when - compiling the service container and won't change at runtime once dumped. + Beware that dumping the contents of the ``$_SERVER`` and ``$_ENV`` variables + or outputting the ``phpinfo()`` contents will display the values of the + environment variables, exposing sensitive information such as the database + credentials. Constants --------- @@ -144,51 +139,19 @@ See :ref:`component-di-parameters-constants` for more details. Miscellaneous Configuration --------------------------- -The ``imports`` directive can be used to pull in parameters stored elsewhere. -Importing a PHP file gives you the flexibility to add whatever is needed -in the container. The following imports a file named ``parameters.php``. - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - imports: - - { resource: parameters.php } +You can mix whatever configuration format you like (YAML, XML and PHP) in +``config/packages/``. Importing a PHP file gives you the flexibility to add +whatever is needed in the container. For instance, you can create a +``drupal.php`` file in which you set a database URL based on Drupal's database +configuration:: - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $loader->import('parameters.php'); + // config/packages/drupal.php -.. note:: - - A resource file can be one of many types. PHP, XML, YAML, INI, and - closure resources are all supported by the ``imports`` directive. - -In ``parameters.php``, tell the service container the parameters that you wish -to set. This is useful when important configuration is in a non-standard -format. The example below includes a Drupal database configuration in -the Symfony service container:: - - // app/config/parameters.php + // import Drupal's configuration include_once('/path/to/drupal/sites/default/settings.php'); - $container->setParameter('drupal.database.url', $db_url); + + // set a app.database_url parameter + $container->setParameter('app.database_url', $db_url); .. _`SetEnv`: http://httpd.apache.org/docs/current/env.html .. _`fastcgi_param`: http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_param diff --git a/configuration/front_controllers_and_kernel.rst b/configuration/front_controllers_and_kernel.rst index 4b361724ca3..7faf233b7de 100644 --- a/configuration/front_controllers_and_kernel.rst +++ b/configuration/front_controllers_and_kernel.rst @@ -1,14 +1,14 @@ .. index:: - single: How the front controller, ``AppKernel`` and environments + single: How the front controller, ``Kernel`` and environments work together Understanding how the Front Controller, Kernel and Environments Work together ============================================================================= -The section :doc:`/configuration/environments` explained the basics -on how Symfony uses environments to run your application with different configuration -settings. This section will explain a bit more in-depth what happens when -your application is bootstrapped. To hook into this process, you need to understand +The section :doc:`/configuration/environments` explained the basics on how +Symfony uses environments to run your application with different configuration +settings. This section will explain a bit more in-depth what happens when your +application is bootstrapped. To hook into this process, you need to understand three parts that work together: * `The Front Controller`_ @@ -18,25 +18,22 @@ three parts that work together: .. note:: Usually, you will not need to define your own front controller or - ``AppKernel`` class as the `Symfony Standard Edition`_ provides - sensible default implementations. - - This documentation section is provided to explain what is going on behind - the scenes. + ``Kernel`` class as Symfony provides sensible default implementations. + This article is provided to explain what is going on behind the scenes. The Front Controller -------------------- -The `front controller`_ is a well-known design pattern; it is a section of -code that *all* requests served by an application run through. +The `front controller`_ is a design pattern; it is a section of code that *all* +requests served by an application run through. -In the `Symfony Standard Edition`_, this role is taken by the `app.php`_ -and `app_dev.php`_ files in the ``web/`` directory. These are the very -first PHP scripts executed when a request is processed. +In the Symfony Skeleton, this role is taken by the ``index.php`` file in the +``public/`` directory. This is the very first PHP script executed when a +request is processed. The main purpose of the front controller is to create an instance of the -``AppKernel`` (more on that in a second), make it handle the request -and return the resulting response to the browser. +``Kernel`` (more on that in a second), make it handle the request and return +the resulting response to the browser. Because every request is routed through it, the front controller can be used to perform global initialization prior to setting up the kernel or @@ -44,38 +41,26 @@ to `decorate`_ the kernel with additional features. Examples include: * Configuring the autoloader or adding additional autoloading mechanisms; * Adding HTTP level caching by wrapping the kernel with an instance of - :ref:`AppCache `; -* Enabling (or skipping) the :doc:`ClassCache `; + :ref:`HttpCache `; * Enabling the :doc:`Debug Component `. -The front controller can be chosen by requesting URLs like: +You can choose the front controller that's used by adding it in the URL, like: .. code-block:: text - http://localhost/app_dev.php/some/path/... + http://localhost/index.php/some/path/... As you can see, this URL contains the PHP script to be used as the front -controller. You can use that to easily switch the front controller or use -a custom one by placing it in the ``web/`` directory (e.g. ``app_cache.php``). - -When using Apache and the `RewriteRule shipped with the Symfony Standard Edition`_, -you can omit the filename from the URL and the RewriteRule will use ``app.php`` -as the default one. +controller. You can use that to easily switch to a custom made front controller +that is located in the ``public/`` directory. -.. note:: +.. seealso:: - Pretty much every other web server should be able to achieve a - behavior similar to that of the RewriteRule described above. - Check your server documentation for details or see + You almost never want to show the front controller in the URL. This is + achieved by configuring the web server, as shown in :doc:`/setup/web_server_configuration`. -.. note:: - - Make sure you appropriately secure your front controllers against unauthorized - access. For example, you don't want to make a debugging environment - available to arbitrary users in your production environment. - -Technically, the `bin/console`_ script used when running Symfony on the command +Technically, the ``bin/console`` script used when running Symfony on the command line is also a front controller, only that is not used for web, but for command line requests. @@ -83,87 +68,79 @@ The Kernel Class ---------------- The :class:`Symfony\\Component\\HttpKernel\\Kernel` is the core of -Symfony. It is responsible for setting up all the bundles that make up +Symfony. It is responsible for setting up all the bundles used by your application and providing them with the application's configuration. It then creates the service container before serving requests in its :method:`Symfony\\Component\\HttpKernel\\HttpKernelInterface::handle` method. -There are two methods declared in the -:class:`Symfony\\Component\\HttpKernel\\KernelInterface` that are -left unimplemented in :class:`Symfony\\Component\\HttpKernel\\Kernel` -and thus serve as `template methods`_: +The kernel used in Symfony apps extends from :class:`Symfony\\Component\\HttpKernel\\Kernel` +and uses the :class:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait`. +The ``Kernel`` class leaves some methods from :class:`Symfony\\Component\\HttpKernel\\KernelInterface` +unimplemented and the ``MicroKernelTrait`` defines several abstract methods, so +you must implement them all: :method:`Symfony\\Component\\HttpKernel\\KernelInterface::registerBundles` It must return an array of all bundles needed to run the application. -:method:`Symfony\\Component\\HttpKernel\\KernelInterface::registerContainerConfiguration` - It loads the application configuration. -To fill these (small) blanks, your application needs to subclass the -Kernel and implement these methods. The resulting class is conventionally -called the ``AppKernel``. +:method:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait::configureRoutes` + It adds individual routes or collections of routes to the application (for + example loading the routes defined in some config file). + +:method:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait::configureContainer` + It loads the application configuration from config files or using the + ``loadFromExtension()`` method and can also register new container parameters + and services. -Again, the Symfony Standard Edition provides an `AppKernel`_ in the ``app/`` -directory. This class uses the name of the environment - which is passed to -the Kernel's :method:`constructor ` +To fill these (small) blanks, your application needs to extend the Kernel class +and use the MicroKernelTrait to implement these methods. Symfony provides by +default that kernel in the ``src/Kernel.php`` file. + +This class uses the name of the environment - which is passed to the Kernel's +:method:`constructor ` method and is available via :method:`Symfony\\Component\\HttpKernel\\Kernel::getEnvironment` - -to decide which bundles to create. The logic for that is in ``registerBundles()``, -a method meant to be extended by you when you start adding bundles to your -application. +to decide which bundles to enable. The logic for that is in ``registerBundles()``. You are, of course, free to create your own, alternative or additional -``AppKernel`` variants. All you need is to adapt your (or add a new) front +``Kernel`` variants. All you need is to adapt your (or add a new) front controller to make use of the new kernel. .. note:: - The name and location of the ``AppKernel`` is not fixed. When - putting multiple Kernels into a single application, - it might therefore make sense to add additional sub-directories, - for example ``app/admin/AdminKernel.php`` and - ``app/api/ApiKernel.php``. All that matters is that your front - controller is able to create an instance of the appropriate kernel. - -Having different ``AppKernels`` might be useful to enable different front -controllers (on potentially different servers) to run parts of your application -independently (for example, the admin UI, the front-end UI and database migrations). + The name and location of the ``Kernel`` is not fixed. When putting + :doc:`multiple kernels into a single application `, + it might therefore make sense to add additional sub-directories, for example + ``src/admin/AdminKernel.php`` and ``src/api/ApiKernel.php``. All that matters + is that your front controller is able to create an instance of the appropriate kernel. .. note:: - There's a lot more the ``AppKernel`` can be used for, for example + There's a lot more the ``Kernel`` can be used for, for example :doc:`overriding the default directory structure `. But odds are high that you don't need to change things like this on the - fly by having several ``AppKernel`` implementations. + fly by having several ``Kernel`` implementations. The Environments ---------------- -As just mentioned, the ``AppKernel`` has to implement another method - -:method:`Symfony\\Component\\HttpKernel\\KernelInterface::registerContainerConfiguration`. -This method is responsible for loading the application's -configuration from the right *environment*. +As just mentioned, the ``Kernel`` has to implement another method - +:method:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait::configureContainer`. +This method is responsible for loading the application's configuration from the +right *environment*. -Environments have been covered extensively -:doc:`in the previous article `, -and you probably remember that the Symfony Standard Edition comes with three -of them - ``dev``, ``prod`` and ``test``. +Environments have been covered extensively :doc:`in the previous article +`, and you probably remember that the Symfony uses +by default three of them - ``dev``, ``prod`` and ``test``. More technically, these names are nothing more than strings passed from the -front controller to the ``AppKernel``'s constructor. This name can then be -used in the :method:`Symfony\\Component\\HttpKernel\\KernelInterface::registerContainerConfiguration` -method to decide which configuration files to load. +front controller to the ``Kernel``'s constructor. This name can then be used in +the ``configureContainer()`` method to decide which configuration files to load. -The Symfony Standard Edition's `AppKernel`_ class implements this method by simply -loading the ``app/config/config_*environment*.yml`` file. You are, of course, -free to implement this method differently if you need a more sophisticated -way of loading your configuration. +Symfony's default ``Kernel`` class implements this method by loading first the +config files found on ``config/packages/*`` and then, the files found on +``config/packages/ENVIRONMENT_NAME/``. You are, of course, free to implement +this method differently if you need a more sophisticated way of loading your +configuration. .. _front controller: https://en.wikipedia.org/wiki/Front_Controller_pattern -.. _Symfony Standard Edition: https://github.com/symfony/symfony-standard -.. _app.php: https://github.com/symfony/symfony-standard/blob/master/web/app.php -.. _app_dev.php: https://github.com/symfony/symfony-standard/blob/master/web/app_dev.php -.. _bin/console: https://github.com/symfony/symfony-standard/blob/master/bin/console -.. _AppKernel: https://github.com/symfony/symfony-standard/blob/master/app/AppKernel.php .. _decorate: https://en.wikipedia.org/wiki/Decorator_pattern -.. _RewriteRule shipped with the Symfony Standard Edition: https://github.com/symfony/symfony-standard/blob/master/web/.htaccess -.. _template methods: https://en.wikipedia.org/wiki/Template_method_pattern diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 9a576fdc10e..5cd697943c2 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -1,40 +1,38 @@ Building your own Framework with the MicroKernelTrait ===================================================== -A :ref:`traditional Symfony app ` contains a sensible -directory structure, various configuration files and an ``AppKernel`` with several -bundles already-registered. This is a fully-featured app that's ready to go. +The default ``Kernel`` class included in Symfony applications uses a +:class:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait` to configure +the bundles, the routes and the service container in the same class. -But did you know, you can create a fully-functional Symfony application in as little -as one file? This is possible thanks to the new -:class:`Symfony\\Bundle\\FrameworkBundle\\Kernel\\MicroKernelTrait`. This allows -you to start with a tiny application, and then add features and structure as you -need to. +This micro-kernel approach is so flexible that let you control your application +structure and features quite easily. A Single-File Symfony Application --------------------------------- -Start with a completely empty directory. Get ``symfony/symfony`` as a dependency +Start with a completely empty directory and install these Symfony components via Composer: .. code-block:: bash - $ composer require symfony/symfony + $ composer require symfony/config symfony/http-kernel \ + symfony/http-foundation symfony/routing \ + symfony/dependency-injection symfony/framework-bundle -Next, create an ``index.php`` file that creates a kernel class and executes it:: +Next, create an ``index.php`` file that defines the kernel class and executes it:: use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\Kernel; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; - // require Composer's autoloader require __DIR__.'/vendor/autoload.php'; - class AppKernel extends Kernel + class Kernel extends BaseKernel { use MicroKernelTrait; @@ -47,7 +45,7 @@ Next, create an ``index.php`` file that creates a kernel class and executes it:: protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader) { - // PHP equivalent of config.yml + // PHP equivalent of config/packages/framework.yaml $c->loadFromExtension('framework', array( 'secret' => 'S0ME_SECRET' )); @@ -68,7 +66,7 @@ Next, create an ``index.php`` file that creates a kernel class and executes it:: } } - $kernel = new AppKernel('dev', true); + $kernel = new Kernel('dev', true); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); @@ -76,7 +74,7 @@ Next, create an ``index.php`` file that creates a kernel class and executes it:: That's it! To test it, you can start the built-in web server: -.. code-block:: bash +.. code-block:: terminal $ php -S localhost:8000 @@ -96,8 +94,8 @@ that define your bundles, your services and your routes: **configureContainer(ContainerBuilder $c, LoaderInterface $loader)** This method builds and configures the container. In practice, you will use ``loadFromExtension`` to configure different bundles (this is the equivalent - of what you see in a normal ``config.yml`` file). You can also register services - directly in PHP or load external configuration files (shown below). + of what you see in a normal ``config/packages/*`` file). You can also register + services directly in PHP or load external configuration files (shown below). **configureRoutes(RouteCollectionBuilder $routes)** Your job in this method is to add routes to the application. The @@ -121,30 +119,30 @@ your ``composer.json`` file to load from there: }, "autoload": { "psr-4": { - "": "src/" + "App\\": "src/" } } } Now, suppose you want to use Twig and load routes via annotations. Instead of -putting *everything* in ``index.php``, create a new ``app/AppKernel.php`` to +putting *everything* in ``index.php``, create a new ``src/Kernel.php`` to hold the kernel. Now it looks like this:: - // app/AppKernel.php + // src/Kernel.php + namespace App; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; - use Symfony\Component\HttpKernel\Kernel; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\RouteCollectionBuilder; use Doctrine\Common\Annotations\AnnotationRegistry; - // require Composer's autoloader $loader = require __DIR__.'/../vendor/autoload.php'; // auto-load annotations AnnotationRegistry::registerLoader(array($loader, 'loadClass')); - class AppKernel extends Kernel + class Kernel extends BaseKernel { use MicroKernelTrait; @@ -164,7 +162,7 @@ hold the kernel. Now it looks like this:: protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader) { - $loader->load(__DIR__.'/config/config.yml'); + $loader->load(__DIR__.'/config/framework.yaml'); // configure WebProfilerBundle only if the bundle is enabled if (isset($this->bundles['WebProfilerBundle'])) { @@ -184,7 +182,7 @@ hold the kernel. Now it looks like this:: } // load the annotation routes - $routes->import(__DIR__.'/../src/App/Controller/', '/', 'annotation'); + $routes->import(__DIR__.'/../src/Controller/', '/', 'annotation'); } // optional, to use the standard Symfony cache directory @@ -196,23 +194,18 @@ hold the kernel. Now it looks like this:: // optional, to use the standard Symfony logs directory public function getLogDir() { - return __DIR__.'/../var/logs'; + return __DIR__.'/../var/log'; } } -.. versionadded:: 3.4 - Support for annotation routing without an external bundle was added in - Symfony 3.4. Prior to version 3.4, you needed to install the - SensioFrameworkExtraBundle. - -Unlike the previous kernel, this loads an external ``app/config/config.yml`` file, +Unlike the previous kernel, this loads an external ``config/framework.yaml`` file, because the configuration started to get bigger: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/framework.yaml framework: secret: S0ME_SECRET templating: @@ -221,7 +214,7 @@ because the configuration started to get bigger: .. code-block:: xml - + loadFromExtension('framework', array( 'secret' => 'S0ME_SECRET', 'templating' => array( @@ -250,10 +243,10 @@ because the configuration started to get bigger: ), )); -This also loads annotation routes from an ``src/App/Controller/`` directory, which +This also loads annotation routes from an ``src/Controller/`` directory, which has one file in it:: - // src/App/Controller/MicroController.php + // src/Controller/MicroController.php namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -274,13 +267,13 @@ has one file in it:: } } -Template files should live in the ``Resources/views`` directory of whatever directory -your *kernel* lives in. Since ``AppKernel`` lives in ``app/``, this template lives -at ``app/Resources/views/micro/random.html.twig``: +Template files should live in the ``Resources/views/`` directory of whatever directory +your *kernel* lives in. Since ``Kernel`` lives in ``src/``, this template lives +at ``src/Resources/views/micro/random.html.twig``: .. code-block:: html+twig - + @@ -292,15 +285,15 @@ at ``app/Resources/views/micro/random.html.twig``: Finally, you need a front controller to boot and run the application. Create a -``web/index.php``:: +``public/index.php``:: - // web/index.php + // public/index.php use Symfony\Component\HttpFoundation\Request; - require __DIR__.'/../app/AppKernel.php'; + require __DIR__.'/../src/Kernel.php'; - $kernel = new AppKernel('dev', true); + $kernel = new Kernel('dev', true); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); @@ -313,38 +306,33 @@ this: .. code-block:: text your-project/ - ├─ app/ - | ├─ AppKernel.php - │ ├─ config/ + ├─ config/ + │ └─ framework.yaml + ├─ public/ + | └─ index.php + ├─ src/ + | ├─ Kernel.php + | ├─ Controller + | | └─ MicroController.php │ └─ Resources | └─ views | └─ micro | └─ random.html.twig - ├─ src/ - │ └─ App - | └─ Controller - | └─ MicroController.php ├─ var/ | ├─ cache/ - │ └─ logs/ + │ └─ log/ ├─ vendor/ │ └─ ... - ├─ web/ - | └─ index.php ├─ composer.json └─ composer.lock As before you can use PHP built-in server: -.. code-block:: bash +.. code-block:: terminal - cd web/ + cd public/ $ php -S localhost:8000 Then see webpage in browser: http://localhost:8000/random/10 - -Hey, that looks a lot like a *traditional* Symfony application! You're right: the -``MicroKernelTrait`` *is* still Symfony: but you can control your structure and -features quite easily. diff --git a/configuration/multiple_kernels.rst b/configuration/multiple_kernels.rst index b6359a99bda..a6d3449685f 100644 --- a/configuration/multiple_kernels.rst +++ b/configuration/multiple_kernels.rst @@ -4,16 +4,21 @@ How To Create Symfony Applications with Multiple Kernels ======================================================== +.. caution:: + + Creating applications with multiple kernels is no longer recommended by + Symfony. Consider creating multiple small applications instead. + In most Symfony applications, incoming requests are processed by the -``web/app.php`` front controller, which instantiates the ``app/AppKernel.php`` +``public/index.php`` front controller, which instantiates the ``src/Kernel.php`` class to create the application kernel that loads the bundles and handles the request to generate the response. -This single kernel approach is a convenient default provided by the Symfony -Standard edition, but Symfony applications can define any number of kernels. -Whereas :doc:`environments ` execute the same -application with different configurations, kernels can execute different parts -of the same application. +This single kernel approach is a convenient default, but Symfony applications +can define any number of kernels. Whereas +:doc:`environments ` execute the same application +with different configurations, kernels can execute different parts of the same +application. These are some of the common use cases for creating multiple kernels: @@ -45,45 +50,46 @@ Step 1) Create a new Front Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Instead of creating the new front controller from scratch, it's easier to -duplicate the existing ones. For example, create ``web/api_dev.php`` from -``web/app_dev.php`` and ``web/api.php`` from ``web/app.php``. - -Then, update the code of the new front controllers to instantiate the new kernel -class instead of the usual ``AppKernel`` class:: +duplicate the existing one. For example, create ``public/api.php`` from +``public/index.php``. - // web/api.php - // ... - $kernel = new ApiKernel('prod', false); - // ... +Then, update the code of the new front controller to instantiate the new kernel +class instead of the usual ``Kernel`` class:: - // web/api_dev.php + // public/api.php // ... - $kernel = new ApiKernel('dev', true); + $kernel = new ApiKernel( + $_SERVER['APP_ENV'] ?? 'dev', + $_SERVER['APP_DEBUG'] ?? ('prod' !== ($_SERVER['APP_ENV'] ?? 'dev')) + ); // ... .. tip:: - Another approach is to keep the existing front controller (e.g. ``app.php`` and - ``app_dev.php``), but add an ``if`` statement to load the different kernel based - on the URL (e.g. if the URL starts with ``/api``, use the ``ApiKernel``). + Another approach is to keep the existing ``index.php`` front controller, but + add an ``if`` statement to load the different kernel based on the URL (e.g. + if the URL starts with ``/api``, use the ``ApiKernel``). Step 2) Create the new Kernel Class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Now you need to define the ``ApiKernel`` class used by the new front controller. -The easiest way to do this is by duplicating the existing ``app/AppKernel.php`` +The easiest way to do this is by duplicating the existing ``src/Kernel.php`` file and make the needed changes. -In this example, the ``ApiKernel`` will load less bundles than AppKernel. Be -sure to also change the location of the cache, logs and configuration files so -they don't collide with the files from ``AppKernel``:: +In this example, the ``ApiKernel`` will load less bundles than the default +Kernel. Be sure to also change the location of the cache, logs and configuration +files so they don't collide with the files from ``src/Kernel.php``:: - // app/ApiKernel.php + // src/ApiKernel.php use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Config\Loader\LoaderInterface; + use Symfony\Component\DependencyInjection\ContainerBuilder; class ApiKernel extends Kernel { + // ... + public function registerBundles() { // load only the bundles strictly needed for the API... @@ -96,59 +102,41 @@ they don't collide with the files from ``AppKernel``:: public function getLogDir() { - return dirname(__DIR__).'/var/logs/api'; + return dirname(__DIR__).'/var/log/api'; } - public function registerContainerConfiguration(LoaderInterface $loader) + public function configureContainer(ContainerBuilder $container, LoaderInterface $loader) { - $loader->load($this->getProjectDir().'/app/config/api/config_'.$this->getEnvironment().'.yml'); - } - } - -In order for the autoloader to find your new ``ApiKernel``, make sure you add it -to your ``composer.json`` autoload section: - -.. code-block:: json - - { - "...": "..." - - "autoload": { - "psr-4": { "": "src/" }, - "classmap": [ "app/AppKernel.php", "app/AppCache.php", "app/ApiKernel.php" ] + // load only the config files strictly needed for the API + $confDir = $this->getProjectDir().'/config'; + $loader->load($confDir.'/api/*'.self::CONFIG_EXTS, 'glob'); + if (is_dir($confDir.'/api/'.$this->environment)) { + $loader->load($confDir.'/api/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); + } } } -Then, run ``composer install`` to dump your new autoload config. - Step 3) Define the Kernel Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Finally, define the configuration files that the new ``ApiKernel`` will load. -According to the above code, this config will live in the ``app/config/api/`` -directory. +According to the above code, this config will live in one or multiple files +stored in ``config/api/`` and ``config/api/ENVIRONMENT_NAME/`` directories. -The new configuration can be created from scratch when you load just a few +The new configuration files can be created from scratch when you load just a few bundles, because it will be very simple. Otherwise, duplicate the existing -config files or better, import them and override the needed options: - -.. code-block:: yaml - - # app/config/api/config_dev.yml - imports: - - { resource: ../config_dev.yml } - - # override option values ... +config files in ``config/packages/`` or better, import them and override the +needed options. Executing Commands with a Different Kernel ------------------------------------------ The ``bin/console`` script used to run Symfony commands always uses the default -``AppKernel`` class to build the application and load the commands. If you need +``Kernel`` class to build the application and load the commands. If you need to execute console commands using the new kernel, duplicate the ``bin/console`` script and rename it (e.g. ``bin/api``). -Then, replace the ``AppKernel`` instantiation by your own kernel instantiation +Then, replace the ``Kernel`` instantiation by your own kernel instantiation (e.g. ``ApiKernel``) and now you can execute commands using the new kernel (e.g. ``php bin/api cache:clear``) Now you can use execute commands using the new kernel. @@ -163,19 +151,19 @@ Rendering Templates Defined in a Different Kernel ------------------------------------------------- If you follow the Symfony Best Practices, the templates of the default kernel -will be stored in ``app/Resources/views/``. Trying to render those templates in -a different kernel will result in a *There are no registered paths for -namespace "__main__"* error. +will be stored in ``templates/``. Trying to render those templates in a +different kernel will result in a *There are no registered paths for namespace +"__main__"* error. In order to solve this issue, add the following configuration to your kernel: .. code-block:: yaml - # api/config/config.yml + # config/api/twig.yaml twig: paths: - # allows to use app/Resources/views/ templates in the ApiKernel - "%kernel.project_dir%/app/Resources/views": ~ + # allows to use api/templates/ dir in the ApiKernel + "%kernel.project_dir%/api/templates": ~ Running Tests Using a Different Kernel -------------------------------------- @@ -198,7 +186,7 @@ return the fully qualified class name of the kernel to use:: { protected static function getKernelClass() { - return 'ApiKernel'; + return 'App\ApiKernel'; } // this is needed because the KernelTestCase class keeps a reference to @@ -219,23 +207,19 @@ Adding more Kernels to the Application If your application is very complex and you create several kernels, it's better to store them in their own directories instead of messing with lots of files in -the default ``app/`` directory: +the default ``src/`` directory: .. code-block:: text project/ - ├─ app/ + ├─ src/ │ ├─ ... - │ ├─ config/ - │ └─ AppKernel.php + │ └─ Kernel.php ├─ api/ │ ├─ ... - │ ├─ config/ │ └─ ApiKernel.php ├─ ... - └─ web/ + └─ public/ ├─ ... - ├─ app.php - ├─ app_dev.php ├─ api.php - └─ api_dev.php + └─ index.php diff --git a/configuration/override_dir_structure.rst b/configuration/override_dir_structure.rst index 7eb24df6e84..22f32aef734 100644 --- a/configuration/override_dir_structure.rst +++ b/configuration/override_dir_structure.rst @@ -11,39 +11,35 @@ directory structure is: .. code-block:: text your-project/ - ├─ app/ - │ ├─ config/ - │ ├─ Resources/ - │ │ └─ views/ - │ └─ ... + ├─ assets/ ├─ bin/ - │ └─ ... + │ └─ console + ├─ config/ + ├─ public/ + │ └─ index.php ├─ src/ │ └─ ... + ├─ templates/ ├─ tests/ - │ └─ ... + ├─ translations/ ├─ var/ │ ├─ cache/ - │ ├─ logs/ - │ └─ ... - ├─ vendor/ + │ ├─ log/ │ └─ ... - └─ web/ - ├─ app.php - └─ ... + └─ vendor/ .. _override-cache-dir: Override the ``cache`` Directory -------------------------------- -You can change the default cache directory by overriding the ``getCacheDir()`` method -in the ``AppKernel`` class of your application:: +You can change the default cache directory by overriding the ``getCacheDir()`` +method in the ``Kernel`` class of your application:: - // app/AppKernel.php + // src/Kernel.php // ... - class AppKernel extends Kernel + class Kernel extends BaseKernel { // ... @@ -73,42 +69,42 @@ Overriding the ``logs`` directory is the same as overriding the ``cache`` directory. The only difference is that you need to override the ``getLogDir()`` method:: - // app/AppKernel.php + // src/Kernel.php // ... - class AppKernel extends Kernel + class Kernel extends Kernel { // ... public function getLogDir() { - return dirname(__DIR__).'/var/'.$this->environment.'/logs'; + return dirname(__DIR__).'/var/'.$this->environment.'/log'; } } -Here you have changed the location of the directory to ``var/{environment}/logs``. +Here you have changed the location of the directory to ``var/{environment}/log``. .. _override-templates-dir: Override the Templates Directory -------------------------------- -If your templates are not stored in the default ``app/Resources/views/`` -directory, use the :ref:`twig.paths ` configuration option to -define your own templates directory (or directories): +If your templates are not stored in the default ``templates/`` directory, use +the :ref:`twig.paths ` configuration option to define your +own templates directory (or directories): .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/packages/twig.yaml twig: # ... - paths: ["%kernel.project_dir%/templates"] + paths: ["%kernel.project_dir%/resources/views"] .. code-block:: xml - + - %kernel.project_dir%/templates + %kernel.project_dir%/resources/views .. code-block:: php - // app/config/config.php + // config/packages/twig.php $container->loadFromExtension('twig', array( 'paths' => array( - '%kernel.project_dir%/templates', + '%kernel.project_dir%/resources/views', ), )); .. _override-web-dir: +.. _override-the-web-directory: -Override the ``web`` Directory ------------------------------- +Override the ``public`` Directory +--------------------------------- -If you need to rename or move your ``web`` directory, the only thing you -need to guarantee is that the path to the ``var`` directory is still correct -in your ``app.php`` and ``app_dev.php`` front controllers. If you simply -renamed the directory, you're fine. But if you moved it in some way, you -may need to modify these paths inside those files:: +If you need to rename or move your ``public`` directory, the only thing you need +to guarantee is that the path to the ``var`` directory is still correct in your +``index.php`` front controller. If you simply renamed the directory, you're +fine. But if you moved it in some way, you may need to modify these paths inside +those files:: - require_once __DIR__.'/../path/to/app/autoload.php'; + require_once __DIR__.'/../path/to/vendor/autoload.php'; -You also need to change the ``extra.symfony-web-dir`` option in the +You also need to change the ``extra.public-dir`` option in the ``composer.json`` file: .. code-block:: json @@ -155,77 +152,24 @@ You also need to change the ``extra.symfony-web-dir`` option in the "...": "...", "extra": { "...": "...", - "symfony-web-dir": "my_new_web_dir" + "public-dir": "my_new_public_dir" } } .. tip:: Some shared hosts have a ``public_html`` web directory root. Renaming - your web directory from ``web`` to ``public_html`` is one way to make + your web directory from ``public`` to ``public_html`` is one way to make your Symfony project work on your shared host. Another way is to deploy your application to a directory outside of your web root, delete your ``public_html`` directory, and then replace it with a symbolic link to - the ``web`` in your project. - -.. note:: - - If you use the AsseticBundle, you need to configure the ``read_from`` option - to point to the correct ``web`` directory: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - - # ... - assetic: - # ... - read_from: '%kernel.project_dir%/../public_html' - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - - // ... - $container->loadFromExtension('assetic', array( - // ... - 'read_from' => '%kernel.project_dir%/../public_html', - )); - - Now you just need to clear the cache and dump the assets again and your - application should work: - - .. code-block:: terminal - - $ php bin/console cache:clear --env=prod - $ php bin/console assetic:dump --env=prod --no-debug + the ``public`` dir in your project. Override the ``vendor`` Directory --------------------------------- -To override the ``vendor`` directory, you need to introduce changes in the -``app/autoload.php`` and ``composer.json`` files. - -The change in the ``composer.json`` will look like this: +To override the ``vendor`` directory, you need to define the ``vendor-dir`` +option in your ``composer.json`` file like this: .. code-block:: json @@ -236,13 +180,6 @@ The change in the ``composer.json`` will look like this: }, } -Then, update the path to the ``autoload.php`` file in ``app/autoload.php``:: - - // app/autoload.php - - // ... - $loader = require '/some/dir/vendor/autoload.php'; - .. tip:: This modification can be of interest if you are working in a virtual environment diff --git a/configuration/using_parameters_in_dic.rst b/configuration/using_parameters_in_dic.rst index 4d45d7738c2..ef090440322 100644 --- a/configuration/using_parameters_in_dic.rst +++ b/configuration/using_parameters_in_dic.rst @@ -37,7 +37,7 @@ Now, examine the results to see this closely: my_bundle: logging: '%kernel.debug%' - # true/false (depends on 2nd parameter of AppKernel), + # true/false (depends on 2nd argument of the Kernel class), # as expected, because %kernel.debug% inside configuration # gets evaluated before being passed to the extension @@ -57,7 +57,7 @@ Now, examine the results to see this closely: - @@ -78,7 +78,7 @@ Now, examine the results to see this closely: $container->loadFromExtension('my_bundle', array( 'logging' => "%kernel.debug%", - // true/false (depends on 2nd parameter of AppKernel), + // true/false (depends on 2nd parameter of Kernel), // as expected, because %kernel.debug% inside configuration // gets evaluated before being passed to the extension ) @@ -93,7 +93,7 @@ Now, examine the results to see this closely: In order to support this use case, the ``Configuration`` class has to be injected with this parameter via the extension as follows:: - namespace AppBundle\DependencyInjection; + namespace App\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -126,7 +126,7 @@ be injected with this parameter via the extension as follows:: And set it in the constructor of ``Configuration`` via the ``Extension`` class:: - namespace AppBundle\DependencyInjection; + namespace App\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -141,16 +141,8 @@ And set it in the constructor of ``Configuration`` via the ``Extension`` class:: } } -.. sidebar:: Setting the Default in the Extension +.. tip:: - There are some instances of ``%kernel.debug%`` usage within a ``Configurator`` - class in TwigBundle and AsseticBundle. However this is because the default - parameter value is set by the Extension class. For example in AsseticBundle, - you can find:: - - $container->setParameter('assetic.debug', $config['debug']); - - The string ``%kernel.debug%`` passed here as an argument handles the - interpreting job to the container which in turn does the evaluation. - Both ways accomplish similar goals. AsseticBundle will not use - ``%kernel.debug%`` but rather the new ``%assetic.debug%`` parameter. + There are some instances of ``%kernel.debug%`` usage within a + ``Configurator`` class for example in TwigBundle. However this is because + the default parameter value is set by the Extension class. diff --git a/console.rst b/console.rst index 3becf9cd559..6ce7b4122ba 100644 --- a/console.rst +++ b/console.rst @@ -9,17 +9,25 @@ The Symfony framework provides lots of commands through the ``bin/console`` scri created with the :doc:`Console component `. You can also use it to create your own commands. +The Console: APP_ENV & APP_DEBUG +--------------------------------- + +Console commands run in the :ref:`environment ` defined in the ``APP_ENV`` +variable of the ``.env`` file, which is ``dev`` by default. It also reads the ``APP_DEBUG`` +value to turn "debug" mode on or off (it defaults to ``1``, which is on). + +To run the command in another environment or debug mode, edit the value of ``APP_ENV`` +and ``APP_DEBUG``. + Creating a Command ------------------ -Commands are defined in classes which should be created in the ``Command`` namespace -of your bundle (e.g. ``AppBundle\Command``) and their names should end with the -``Command`` suffix. +Commands are defined in classes extending +:class:`Symfony\\Component\\Console\\Command\\Command`. For example, you may +want a command to create a user:: -For example, you may want a command to create a user:: - - // src/AppBundle/Command/CreateUserCommand.php - namespace AppBundle\Command; + // src/Command/CreateUserCommand.php + namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -64,16 +72,10 @@ method. Then you can optionally define a help message and the Registering the Command ----------------------- -Symfony commands must be registered before using them. In order to be registered -automatically, a command must be: - -#. Stored in a directory called ``Command/``; -#. Defined in a class whose name ends with ``Command``; -#. Defined in a class that extends from - :class:`Symfony\\Component\\Console\\Command\\Command`. - -If you can't meet these conditions for a command, the alternative is to manually -:doc:`register the command as a service `. +Symfony commands must be registered as services and :doc:`tagged ` +with the ``console.command`` tag. If you're using the +:ref:`default services.yaml configuration `, +this is already done for you, thanks to :ref:`autoconfiguration `. Executing the Command --------------------- @@ -84,12 +86,6 @@ After configuring and registering the command, you can execute it in the termina $ php bin/console app:create-user -.. caution:: - - Symfony also looks in the ``Command/`` directory of bundles for commands - that are not registered as a service. But this auto discovery is deprecated - since Symfony 3.4 and won't be supported anymore in Symfony 4.0. - As you might expect, this command will do nothing as you didn't write any logic yet. Add your own logic inside the ``execute()`` method, which has access to the input stream (e.g. options and arguments) and the output stream (to write @@ -175,11 +171,11 @@ Getting Services from the Service Container To actually create a new user, the command has to access to some :doc:`services `. Since your command is already registered as a service, you can use normal dependency injection. Imagine you have a -``AppBundle\Service\UserManager`` service that you want to access:: +``App\Service\UserManager`` service that you want to access:: // ... use Symfony\Component\Console\Command\Command; - use AppBundle\Service\UserManager; + use App\Service\UserManager; class CreateUserCommand extends Command { @@ -236,10 +232,10 @@ useful one is the :class:`Symfony\\Component\\Console\\Tester\\CommandTester` class. It uses special input and output classes to ease testing without a real console:: - // tests/AppBundle/Command/CreateUserCommandTest.php - namespace Tests\AppBundle\Command; + // tests/Command/CreateUserCommandTest.php + namespace App\Tests\Command; - use AppBundle\Command\CreateUserCommand; + use App\Command\CreateUserCommand; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Console\Tester\CommandTester; @@ -301,7 +297,6 @@ you can extend your test from $kernel->boot(); $application = new Application($kernel); - $application->add(new CreateUserCommand()); $command = $application->find('app:create-user'); $commandTester = new CommandTester($command); diff --git a/console/calling_commands.rst b/console/calling_commands.rst index 78759b70947..cfa3adf9966 100644 --- a/console/calling_commands.rst +++ b/console/calling_commands.rst @@ -6,7 +6,7 @@ user to remember the order of execution, you can call it directly yourself. This is also useful if you want to create a "meta" command that just runs a bunch of other commands (for instance, all commands that need to be run when the project's code has changed on the production servers: clearing the cache, -generating Doctrine2 proxies, dumping Assetic assets, ...). +generating Doctrine2 proxies, dumping web assets, ...). Calling a command from another one is straightforward:: diff --git a/console/coloring.rst b/console/coloring.rst index 2970b1101a0..75458394abb 100644 --- a/console/coloring.rst +++ b/console/coloring.rst @@ -43,7 +43,7 @@ It is possible to define your own styles using the $outputStyle = new OutputFormatterStyle('red', 'yellow', array('bold', 'blink')); $output->getFormatter()->setStyle('fire', $outputStyle); - $output->writeln('foo'); + $output->writeln('foo'); Available foreground and background colors are: ``black``, ``red``, ``green``, ``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``. @@ -54,7 +54,7 @@ are swapped) and ``conceal`` (sets the foreground color to transparent, making the typed text invisible - although it can be selected and copied; this option is commonly used when asking the user to type sensitive information). -You can also set these colors and options directly inside the tagname:: +You can also set these colors and options directly inside the tag name:: // green text $output->writeln('foo'); diff --git a/console/command_in_controller.rst b/console/command_in_controller.rst index e01706fdfba..24970c65943 100644 --- a/console/command_in_controller.rst +++ b/console/command_in_controller.rst @@ -24,8 +24,8 @@ Imagine you want to send spooled Swift Mailer messages by :doc:`using the swiftmailer:spool:send command `. Run this command from inside your controller via:: - // src/AppBundle/Controller/SpoolController.php - namespace AppBundle\Controller; + // src/Controller/SpoolController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -76,8 +76,8 @@ First, require the package: Now, use it in your controller:: - // src/AppBundle/Controller/SpoolController.php - namespace AppBundle\Controller; + // src/Controller/SpoolController.php + namespace App\Controller; use SensioLabs\AnsiConverter\AnsiToHtmlConverter; use Symfony\Component\Console\Output\BufferedOutput; diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index a5533ef3b82..d319b2172df 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -4,15 +4,10 @@ How to Define Commands as Services ================================== -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, your command classes are already registered as services. Great! This is the recommended setup. -Symfony also looks in the ``Command/`` directory of each bundle for commands -non registered as a service and automatically registers those classes as -commands. However this auto-registration was deprecated in Symfony 3.4. In -Symfony 4.0, commands won't be auto-registered anymore. - .. note:: You can also manually register your command as a service by configuring the service @@ -26,7 +21,7 @@ using normal :ref:`dependency injection `. For example, suppose you want to log something from within your command:: - namespace AppBundle\Command; + namespace App\Command; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; @@ -59,7 +54,7 @@ For example, suppose you want to log something from within your command:: } } -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, 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. @@ -76,9 +71,6 @@ works! You can call the ``app:sunshine`` command and start logging. Lazy Loading ------------ -.. versionadded:: 3.4 - Support for command lazy loading was introduced in Symfony 3.4. - To make your command lazily loaded, either define its ``$defaultName`` static property:: class SunshineCommand extends Command @@ -94,33 +86,32 @@ Or set the ``command`` attribute on the ``console.command`` tag in your service .. code-block:: yaml + # config/services.yaml services: - - AppBundle\Command\SunshineCommand: + App\Command\SunshineCommand: tags: - { name: 'console.command', command: 'app:sunshine' } # ... .. code-block:: xml + - - + - .. code-block:: php - use AppBundle\Command\SunshineCommand; - + // config/services.php + use App\Command\SunshineCommand; //... $container diff --git a/console/hide_commands.rst b/console/hide_commands.rst index d7c9b011e94..827f93a0a4a 100644 --- a/console/hide_commands.rst +++ b/console/hide_commands.rst @@ -11,8 +11,8 @@ executed through scheduled tasks, etc. In those cases, you can define the command as **hidden** by setting the ``setHidden()`` method to ``true`` in the command configuration:: - // src/AppBundle/Command/LegacyCommand.php - namespace AppBundle\Command; + // src/Command/LegacyCommand.php + namespace App\Command; use Symfony\Component\Console\Command\Command; diff --git a/console/lazy_commands.rst b/console/lazy_commands.rst index 497b3cd33a0..dab96c9bda6 100644 --- a/console/lazy_commands.rst +++ b/console/lazy_commands.rst @@ -1,9 +1,6 @@ How to Make Commands Lazily Loaded ================================== -.. versionadded:: 3.4 - Support for command lazy loading was introduced in Symfony 3.4. - .. note:: If you are using the Symfony full-stack framework, you are probably looking for @@ -16,7 +13,7 @@ The traditional way of adding commands to your application is to use In order to lazy-load commands, you need to register an intermediate loader which will be responsible for returning ``Command`` instances:: - use AppBundle\Command\HeavyCommand; + use App\Command\HeavyCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\CommandLoader\FactoryCommandLoader; diff --git a/console/lockable_trait.rst b/console/lockable_trait.rst index ba3bea637cf..bb6ac39c5b6 100644 --- a/console/lockable_trait.rst +++ b/console/lockable_trait.rst @@ -1,9 +1,6 @@ Prevent Multiple Executions of a Console Command ================================================ -.. versionadded:: 3.2 - The ``LockableTrait`` was introduced in Symfony 3.2. - A simple but effective way to prevent multiple executions of the same command in a single server is to use `locks`_. The :doc:`Lock component ` provides multiple classes to create locks based on the filesystem (:ref:`FlockStore `), diff --git a/console/logging.rst b/console/logging.rst deleted file mode 100644 index 000165112b0..00000000000 --- a/console/logging.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. index:: - single: Console; Enabling logging - -How to Enable Logging in Console Commands -========================================= - -In Symfony versions prior to 3.3, the Console component didn't provide any -logging capabilities out of the box and you had to implement your own exception -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 ` service to -log messages. diff --git a/console/request_context.rst b/console/request_context.rst index 8760379aa9c..bcd811129d3 100644 --- a/console/request_context.rst +++ b/console/request_context.rst @@ -21,8 +21,8 @@ Configuring the Request Context Globally To configure the Request Context - which is used by the URL Generator - you can redefine the parameters it uses as default values to change the default host -(localhost) and scheme (http). You can also configure the base path if Symfony -is not running in the root directory. +(``localhost``) and scheme (``http``). You can also configure the base path if +Symfony is not running in the root directory. Note that this does not impact URLs generated via normal web requests, since those will override the defaults. @@ -31,7 +31,7 @@ will override the defaults. .. code-block:: yaml - # app/config/parameters.yml + # config/services.yaml parameters: router.request_context.host: example.org router.request_context.scheme: https @@ -39,7 +39,7 @@ will override the defaults. .. code-block:: xml - + @@ -54,7 +54,7 @@ will override the defaults. .. code-block:: php - // app/config/parameters.php + // config/services.php $container->setParameter('router.request_context.host', 'example.org'); $container->setParameter('router.request_context.scheme', 'https'); $container->setParameter('router.request_context.base_url', 'my/path'); @@ -62,23 +62,30 @@ will override the defaults. Configuring the Request Context per Command ------------------------------------------- -To change it only in one command you can simply fetch the Request Context -from the ``router`` service and override its settings:: - - // src/AppBundle/Command/DemoCommand.php +To change it only in one command you can fetch the Request Context from the +router service and override its settings:: + // src/Command/DemoCommand.php + use Symfony\Component\Routing\RouterInterface; // ... + class DemoCommand extends ContainerAwareCommand { + private $router; + + public function __construct(RouterInterface $router) + { + $this->router = $router; + } + protected function execute(InputInterface $input, OutputInterface $output) { - $router = $this->getContainer()->get('router'); - $context = $router->getContext(); + $context = $this->router->getContext(); $context->setHost('example.com'); $context->setScheme('https'); $context->setBaseUrl('my/path'); - $url = $router->generate('route-name', array('param-name' => 'param-value')); + $url = $this->router->generate('route-name', array('param-name' => 'param-value')); // ... } } diff --git a/console/style.rst b/console/style.rst index d41b7217af0..96de7e4198c 100644 --- a/console/style.rst +++ b/console/style.rst @@ -10,8 +10,8 @@ questions to the user involves a lot of repetitive code. Consider for example the code used to display the title of the following command:: - // src/AppBundle/Command/GreetCommand.php - namespace AppBundle\Command; + // src/Command/GreetCommand.php + namespace App\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputInterface; @@ -50,8 +50,8 @@ class and pass the ``$input`` and ``$output`` variables as its arguments. Then, you can start using any of its helpers, such as ``title()``, which displays the title of the command:: - // src/AppBundle/Command/GreetCommand.php - namespace AppBundle\Command; + // src/Command/GreetCommand.php + namespace App\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Style\SymfonyStyle; @@ -339,7 +339,7 @@ If you don't like the design of the commands that use the Symfony Style, you can define your own set of console styles. Just create a class that implements the :class:`Symfony\\Component\\Console\\Style\\StyleInterface`:: - namespace AppBundle\Console; + namespace App\Console; use Symfony\Component\Console\Style\StyleInterface; @@ -352,9 +352,9 @@ Then, instantiate this custom class instead of the default ``SymfonyStyle`` in your commands. Thanks to the ``StyleInterface`` you won't need to change the code of your commands to change their appearance:: - namespace AppBundle\Console; + namespace App\Console; - use AppBundle\Console\CustomStyle; + use App\Console\CustomStyle; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; diff --git a/console/usage.rst b/console/usage.rst deleted file mode 100644 index 21cdc42751a..00000000000 --- a/console/usage.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. index:: - single: Console; Usage - -How to Use the Console -====================== - -The :doc:`/components/console/usage` page of the components documentation looks -at the global console options. When you use the console as part of the full-stack -framework, some additional global options are available as well. - -By default, console commands run in the ``dev`` environment and you may want to -change this for some commands. For example, you may want to run some commands in -the ``prod`` environment for performance reasons. Also, the result of some -commands will be different depending on the environment. For example, the -``cache:clear`` command will clear and warm up the cache for the specified -environment only: - -.. code-block:: terminal - - # clear (and warm up) the cache of the 'prod' environment - $ php bin/console cache:clear --env=prod - - # this is equivalent: - $ php bin/console cache:clear --no-warmup -e prod - -In addition to changing the environment, you can also choose to disable debug mode. -This can be useful where you want to run commands in the ``dev`` environment -but avoid the performance hit of collecting debug data: - -.. code-block:: terminal - - $ php bin/console list --no-debug diff --git a/contributing/code/reproducer.rst b/contributing/code/reproducer.rst index 26049b094c9..49a55081ad8 100644 --- a/contributing/code/reproducer.rst +++ b/contributing/code/reproducer.rst @@ -33,21 +33,23 @@ Reproducing Complex Bugs ------------------------ If the bug is related to the Symfony Framework or if it's too complex to create -a PHP script, it's better to reproduce the bug by forking the Symfony Standard -edition. To do so: - -#. Go to https://github.com/symfony/symfony-standard and click on the **Fork** - button to make a fork of that repository or go to your already forked copy. -#. Clone the forked repository into your computer: - ``git clone git://github.com/YOUR-GITHUB-USERNAME/symfony-standard.git`` -#. Browse the project and create a new branch (e.g. ``issue_23567``, - ``reproduce_23657``, etc.) +a PHP script, it's better to reproduce the bug by creating a new project. To do so: + +#. Create a new project: + +.. code-block:: terminal + + $ composer create-project symfony/skeleton bug_app + #. Add and commit the changes generated by Symfony. #. Now you must add the minimum amount of code to reproduce the bug. This is the trickiest part and it's explained a bit more later. -#. Add, commit and push all your own changes. +#. Add and commit your changes. +#. Create a `new repository`_ on GitHub (give it any name). +#. Follow the instructions on GitHub to add the ``origin`` remote to your local project + and push it. #. Add a comment in your original issue report to share the URL of your forked - project (e.g. ``https://github.com/YOUR-GITHUB-USERNAME/symfony-standard/tree/issue_23567``) + project (e.g. ``https://github.com/YOUR-GITHUB-USERNAME/symfony_issue_23567``) and, if necessary, explain the steps to reproduce (e.g. "browse this URL", "fill in this data in the form and submit it", etc.) @@ -56,23 +58,24 @@ Adding the Minimum Amount of Code Possible The key to create a bug reproducer is to solely focus on the feature that you suspect is failing. For example, imagine that you suspect that the bug is related -to a route definition. Then, after forking the Symfony Standard Edition: +to a route definition. Then, after creating your project: #. Don't edit any of the default Symfony configuration options. #. Don't copy your original application code and don't use the same structure - of bundles, controllers, actions, etc. as in your original application. -#. Open the default controller class of the AppBundle and add your routing - definition using annotations. + of controllers, actions, etc. as in your original application. +#. Create a simple controller and add your routing definition that shows the bug. #. Don't create or modify any other file. -#. Execute the ``server:run`` command and browse the previously defined route - to see if the bug appears or not. +#. Execute ``composer require server`` and use the ``server:run`` command to browse + to the new route and see if the bug appears or not. #. If you can see the bug, you're done and you can already share the code with us. #. If you can't see the bug, you must keep making small changes. For example, if your original route was defined using XML, forget about the previous route - annotation and define the route using XML instead. Or maybe your application - uses bundle inheritance and that's where the real bug is. Then, forget about - AppBundle and quickly generate a new AppParentBundle, make AppBundle inherit - from it and test if the route is working. + and define the route using XML instead. Or maybe your application + registers some event listeners and that's where the real bug is. In that case, + add an event listener that's similar to your real app to see if you can find + the bug. + +In short, the idea is to keep adding small and incremental changes to a new project +until you can reproduce the bug. -In short, the idea is to keep adding small and incremental changes to the default -Symfony Standard edition until you can reproduce the bug. +.. _`new repository`: https://github.com/new diff --git a/contributing/documentation/standards.rst b/contributing/documentation/standards.rst index b0471945feb..b302ece435d 100644 --- a/contributing/documentation/standards.rst +++ b/contributing/documentation/standards.rst @@ -54,8 +54,6 @@ Code Examples * The code examples should look real for a web application context. Avoid abstract or trivial examples (``foo``, ``bar``, ``demo``, etc.); * The code should follow the :doc:`Symfony Best Practices `. - Unless the example requires a custom bundle, make sure to always use the - ``AppBundle`` bundle to store your code; * Use ``Acme`` when the code requires a vendor name; * Use ``example.com`` as the domain of sample URLs and ``example.org`` and ``example.net`` when additional domains are required. All of these domains are @@ -132,7 +130,7 @@ Files and Directories --------------------- * When referencing directories, always add a trailing slash to avoid confusions - with regular files (e.g. "execute the ``console`` script located at the ``app/`` + with regular files (e.g. "execute the ``console`` script located at the ``bin/`` directory"). * When referencing file extensions explicitly, you should include a leading dot for every extension (e.g. "XML files use the ``.xml`` extension"). diff --git a/controller.rst b/controller.rst index 73511fbb265..03a5c1443dd 100644 --- a/controller.rst +++ b/controller.rst @@ -4,42 +4,12 @@ Controller ========== -A controller is a PHP function you create that reads information from the Symfony's +A controller is a PHP function you create that reads information from the ``Request`` object and creates and returns a ``Response`` object. The response could be an HTML page, JSON, XML, a file download, a redirect, a 404 error or anything else you can dream up. The controller executes whatever arbitrary logic *your application* needs to render the content of a page. -See how simple this is by looking at a Symfony controller in action. -This renders a page that prints a lucky (random) number:: - - // src/AppBundle/Controller/LuckyController.php - namespace AppBundle\Controller; - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\Routing\Annotation\Route; - - class LuckyController - { - /** - * @Route("/lucky/number") - */ - public function numberAction() - { - $number = mt_rand(0, 100); - - return new Response( - 'Lucky number: '.$number.'' - ); - } - } - -But in the real world, your controller will probably do a lot of work in order to -create the response. It might read information from the request, load a database -resource, send an email or set information on the user's session. -But in all cases, the controller will eventually return the ``Response`` object -that will be delivered back to the client. - .. tip:: If you haven't already created your first working page, check out @@ -55,8 +25,8 @@ While a controller can be any PHP callable (a function, method on an object, or a ``Closure``), a controller is usually a method inside a controller class:: - // src/AppBundle/Controller/LuckyController.php - namespace AppBundle\Controller; + // src/Controller/LuckyController.php + namespace App\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -64,9 +34,9 @@ class:: class LuckyController { /** - * @Route("/lucky/number/{max}") + * @Route("/lucky/number/{max}", name="app_lucky_number") */ - public function numberAction($max) + public function number($max) { $number = mt_rand(0, $max); @@ -76,7 +46,7 @@ class:: } } -The controller is the ``numberAction()`` method, which lives inside a +The controller is the ``number()`` method, which lives inside a controller class ``LuckyController``. This controller is pretty straightforward: @@ -88,13 +58,11 @@ This controller is pretty straightforward: the ``use`` keyword imports the ``Response`` class, which the controller must return. -* *line 7*: The class can technically be called anything - but should end in the - word ``Controller`` (this isn't *required*, but some shortcuts rely on this). +* *line 7*: The class can technically be called anything, but it's suffixed + with ``Controller`` by convention. -* *line 12*: Each action method in a controller class is suffixed with ``Action`` - (again, this isn't *required*, but some shortcuts rely on this). This method - is allowed to have a ``$max`` argument thanks to the ``{max}`` - :doc:`wildcard in the route `. +* *line 12*: The action method is allowed to have a ``$max`` argument thanks to the + ``{max}`` :doc:`wildcard in the route `. * *line 16*: The controller creates and returns a ``Response`` object. @@ -105,7 +73,8 @@ Mapping a URL to a Controller ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to *view* the result of this controller, you need to map a URL to it via -a route. This was done above with the ``@Route("/lucky/number/{max}")`` annotation. +a route. This was done above with the ``@Route("/lucky/number/{max}")`` +:ref:`route annotation `. To see your page, go to this URL in your browser: @@ -121,20 +90,23 @@ For more information on routing, see :doc:`/routing`. The Base Controller Classes & Services -------------------------------------- -For convenience, Symfony comes with two optional base +To make life nicer, Symfony comes with two optional base :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` and -:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController` -classes. You can extend either to get access to a number of `helper methods`_. +:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController`. +You can extend either to get access to some `helper methods`_. -Add the ``use`` statement atop the ``Controller`` class and then modify -``LuckyController`` to extend it:: +Add the ``use`` statement atop your controller class and then modify +``LuckyController`` to extend it: - // src/AppBundle/Controller/LuckyController.php - namespace AppBundle\Controller; +.. code-block:: diff - use Symfony\Bundle\FrameworkBundle\Controller\Controller; + // src/Controller/LuckyController.php + namespace App\Controller; - class LuckyController extends Controller + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + - class LuckyController + + class LuckyController extends Controller { // ... } @@ -146,14 +118,12 @@ and many others that you'll learn about next. .. tip:: - You can extend either ``Controller`` or ``AbstractController``. The difference - is that when you extend ``AbstractController``, you can't access services directly - via ``$this->get()`` or ``$this->container->get()``. This forces you to write - more robust code to access services. But if you *do* need direct access to the - container, using ``Controller`` is fine. - -.. versionadded:: 3.3 - The ``AbstractController`` class was added in Symfony 3.3. + What's the difference between ``Controller`` or ``AbstractController``? Not much: + both are identical, except that ``AbstractController`` is more restrictive: it + does not allow you to access services directly via ``$this->get()`` or + ``$this->container->get()``. This forces you to write more robust code to access + services. But if you *do* need direct access to the container, using ``Controller`` + is fine. .. index:: single: Controller; Redirecting @@ -164,7 +134,7 @@ Generating URLs The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::generateUrl` method is just a helper method that generates the URL for a given route:: - $url = $this->generateUrl('blog_show', array('slug' => 'slug-value')); + $url = $this->generateUrl('app_lucky_number', array('max' => 10)); Redirecting ~~~~~~~~~~~ @@ -172,42 +142,33 @@ Redirecting If you want to redirect the user to another page, use the ``redirectToRoute()`` and ``redirect()`` methods:: - public function indexAction() + use Symfony\Component\HttpFoundation\RedirectResponse; + + // ... + public function index() { // redirects to the "homepage" route return $this->redirectToRoute('homepage'); + // redirectToRoute is a shortcut for: + // return new RedirectResponse($this->generateUrl('homepage')); + // does a permanent - 301 redirect return $this->redirectToRoute('homepage', array(), 301); - // redirects to a route with parameters - return $this->redirectToRoute('blog_show', array('slug' => 'my-page')); + // redirect to a route with parameters + return $this->redirectToRoute('app_lucky_number', array('max' => 10)); // redirects externally return $this->redirect('http://symfony.com/doc'); } -For more information, see the :doc:`Routing article `. - .. caution:: The ``redirect()`` method does not check its destination in any way. If you - redirect to some URL provided by the end-users, your application may be open + redirect to a URL provided by end-users, your application may be open to the `unvalidated redirects security vulnerability`_. -.. tip:: - - The ``redirectToRoute()`` method is simply a shortcut that creates a - ``Response`` object that specializes in redirecting the user. It's - equivalent to:: - - use Symfony\Component\HttpFoundation\RedirectResponse; - - public function indexAction() - { - return new RedirectResponse($this->generateUrl('homepage')); - } - .. index:: single: Controller; Rendering templates @@ -220,31 +181,20 @@ If you're serving HTML, you'll want to render a template. The ``render()`` method renders a template **and** puts that content into a ``Response`` object for you:: - // renders app/Resources/views/lucky/number.html.twig + // renders templates/lucky/number.html.twig return $this->render('lucky/number.html.twig', array('number' => $number)); -Templates can also live in deeper sub-directories. Just try to avoid -creating unnecessarily deep structures:: - - // renders app/Resources/views/lottery/lucky/number.html.twig - return $this->render('lottery/lucky/number.html.twig', array( - 'number' => $number, - )); - -The Symfony templating system and Twig are explained more in the +Templating and Twig are explained more in the :doc:`Creating and Using Templates article `. .. index:: single: Controller; Accessing services .. _controller-accessing-services: +.. _accessing-other-services: -Fetching Services as Controller Arguments -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 3.3 - The ability to type-hint a controller argument in order to receive a service - was added in Symfony 3.3. +Fetching Services +~~~~~~~~~~~~~~~~~ Symfony comes *packed* with a lot of useful objects, called :doc:`services `. These are used for rendering templates, sending emails, querying the database and @@ -259,7 +209,7 @@ If you need a service in a controller, just type-hint an argument with its class /** * @Route("/lucky/number/{max}") */ - public function numberAction($max, LoggerInterface $logger) + public function number($max, LoggerInterface $logger) { $logger->info('We are logging!'); // ... @@ -281,20 +231,22 @@ the argument by its name: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... # explicitly configure the service - AppBundle\Controller\LuckyController: + App\Controller\LuckyController: public: true bind: # for any $logger argument, pass this specific service $logger: '@monolog.logger.doctrine' + # for any $projectDir argument, pass this parameter value + $projectDir: '%kernel.project_dir%' .. code-block:: xml - + - + + %kernel.project_dir% .. code-block:: php - // app/config/services.php - use AppBundle\Controller\LuckyController; + // config/services.php + use App\Controller\LuckyController; use Symfony\Component\DependencyInjection\Reference; $container->register(LuckyController::class) ->setPublic(true) ->setBindings(array( '$logger' => new Reference('monolog.logger.doctrine'), + '$projectDir' => '%kernel.project_dir%' )) ; You can of course also use normal :ref:`constructor injection ` in your controllers. -.. caution:: - - You can *only* pass *services* to your controller arguments in this way. It's not - possible, for example, to pass a service parameter as a controller argument, - even by using ``bind``. If you need a parameter, use the ``$this->getParameter('kernel.debug')`` - shortcut or pass the value through your controller's ``__construct()`` method - and specify its value with ``bind``. +.. versionadded:: 4.1 + The ability to bind scalar values to controller arguments was introduced in + Symfony 4.1. Previously you could only bind services. For more information about services, see the :doc:`/service_container` article. -.. _controller-service-arguments-tag: - -.. note:: - If this isn't working, make sure your controller is registered as a service, - is :ref:`autoconfigured ` and extends either - :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` or - :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController`. If - you use the :ref:`services.yml configuration from the Symfony Standard Edition `, - then your controllers are already registered as services and autoconfigured. +Generating Controllers +---------------------- - If you're not using the default configuration, you can tag your service manually - with ``controller.service_arguments``. - -.. _accessing-other-services: -.. _controller-access-services-directly: +To save time, you can also tell Symfony to generate a new controller class: -Accessing the Container Directly -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you extend the base ``Controller`` class, you can access any Symfony service -via the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::get` -method. Here are several common services you might need:: - - $templating = $this->get('templating'); - - $router = $this->get('router'); +.. code-block:: terminal - $mailer = $this->get('mailer'); + $ php bin/console make:controller BrandNewController - // you can also fetch parameters - $someParameter = $this->getParameter('some_parameter'); + created: src/Controller/BrandNewController.php -If you receive an error like: +If you want to generate an entire CRUD from a Doctrine :doc:`entity `, +use: -.. code-block:: text +.. code-block:: terminal - You have requested a non-existent service "my_service_id" + $ php bin/console make:crud Product -Check to make sure the service exists (use :ref:`debug:container `) -and that it's :ref:`public `. +.. versionadded:: 1.2 + The ``make:crud`` command was introduced in MakerBundle 1.2. .. index:: single: Controller; Managing errors @@ -388,23 +318,27 @@ and that it's :ref:`public `. Managing Errors and 404 Pages ----------------------------- -When things are not found, you should play well with the HTTP protocol and -return a 404 response. To do this, you'll throw a special type of exception. -If you're extending the base ``Controller`` or the base ``AbstractController`` -class, do the following:: +When things are not found, you should return a 404 response. To do this, throw a +special type of exception:: + + use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - public function indexAction() + // ... + public function index() { // retrieve the object from database $product = ...; if (!$product) { throw $this->createNotFoundException('The product does not exist'); + + // the above is just a shortcut for: + // throw new NotFoundHttpException('The product does not exist'); } return $this->render(...); } -The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createNotFoundException` +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerTrait::createNotFoundException` method is just a shortcut to create a special :class:`Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException` object, which ultimately triggers a 404 HTTP response inside Symfony. @@ -418,15 +352,11 @@ HTTP status code:: throw new \Exception('Something went wrong!'); In every case, an error page is shown to the end user and a full debug -error page is shown to the developer (i.e. when you're using the ``app_dev.php`` -front controller - see :ref:`page-creation-environments`). +error page is shown to the developer (i.e. when you're in "Debug" mode - see +:ref:`page-creation-environments`). -You'll want to customize the error page your user sees. To do that, see -the :doc:`/controller/error_pages` article. - -.. index:: - single: Controller; The session - single: Session +To customize the error page that's shown to the user, see the +:doc:`/controller/error_pages` article. .. _controller-request-argument: @@ -440,7 +370,7 @@ object. To get it in your controller, just add it as an argument and use Symfony\Component\HttpFoundation\Request; - public function indexAction(Request $request, $firstName, $lastName) + public function index(Request $request, $firstName, $lastName) { $page = $request->query->get('page', 1); @@ -450,23 +380,41 @@ object. To get it in your controller, just add it as an argument and :ref:`Keep reading ` for more information about using the Request object. +.. index:: + single: Controller; The session + single: Session + +.. _session-intro: + Managing the Session -------------------- -Symfony provides a nice session object that you can use to store information -about the user between requests. By default, Symfony stores the token in a -cookie and writes the attributes to a file by using native PHP sessions. +Symfony provides a session service that you can use to store information +about the user between requests. Session storage and other configuration can +be controlled under the :ref:`framework.session configuration `. + +First, activate the session by uncommenting the ``session`` key in ``config/packages/framework.yaml``: -.. versionadded:: 3.3 - The ability to request a ``Session`` instance in controllers was introduced - in Symfony 3.3. +.. code-block:: diff -To retrieve the session, add the :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface` -type-hint to your argument and Symfony will provide you with a session:: + # config/packages/framework.yaml + framework: + # ... + + - #session: + - # # The native PHP session handler will be used + - # handler_id: ~ + + session: + + # The native PHP session handler will be used + + handler_id: ~ + # ... + +To get the session, add an argument and type-hint it with +:class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface`:: use Symfony\Component\HttpFoundation\Session\SessionInterface; - public function indexAction(SessionInterface $session) + public function index(SessionInterface $session) { // stores an attribute for reuse during a later user request $session->set('foo', 'bar'); @@ -483,7 +431,9 @@ Stored attributes remain in the session for the remainder of that user's session .. tip:: Every ``SessionInterface`` implementation is supported. If you have your - own implementation, type-hint this in the arguments instead. + own implementation, type-hint this in the argument instead. + +For more info, see :doc:`/session`. .. index:: single: Session; Flash messages @@ -500,7 +450,7 @@ For example, imagine you're processing a :doc:`form ` submission:: use Symfony\Component\HttpFoundation\Request; - public function updateAction(Request $request) + public function update(Request $request) { // ... @@ -530,7 +480,7 @@ read any flash messages from the session using ``app.flashes()``: .. code-block:: html+twig - {# app/Resources/views/base.html.twig #} + {# templates/base.html.twig #} {# you can read and display just one flash message type... #} {% for message in app.flashes('notice') %} @@ -550,7 +500,7 @@ read any flash messages from the session using ``app.flashes()``: .. code-block:: html+php - + // you can read and display just one flash message type... getFlashBag()->get('notice') as $message): ?> @@ -568,15 +518,9 @@ read any flash messages from the session using ``app.flashes()``: -.. versionadded:: 3.3 - The ``app.flashes()`` Twig function was introduced in Symfony 3.3. Prior, - you had to use ``app.session.flashBag()``. - -.. note:: - - It's common to use ``notice``, ``warning`` and ``error`` as the keys of the - different types of flash messages, but you can use any key that fits your - needs. +It's common to use ``notice``, ``warning`` and ``error`` as the keys of the +different types of flash messages, but you can use any key that fits your +needs. .. tip:: @@ -592,13 +536,13 @@ read any flash messages from the session using ``app.flashes()``: The Request and Response Object ------------------------------- -As mentioned :ref:`earlier `, the framework will +As mentioned :ref:`earlier `, Symfony will pass the ``Request`` object to any controller argument that is type-hinted with the ``Request`` class:: use Symfony\Component\HttpFoundation\Request; - public function indexAction(Request $request) + public function index(Request $request) { $request->isXmlHttpRequest(); // is it an Ajax request? @@ -631,10 +575,7 @@ some nice methods for getting and setting response headers. The header names are normalized so that using ``Content-Type`` is equivalent to ``content-type`` or even ``content_type``. -The only requirement for a controller is to return a ``Response`` object. -The :class:`Symfony\\Component\\HttpFoundation\\Response` class is an -abstraction around the HTTP response - the text-based message filled with -headers and content that's sent back to the client:: +The only requirement for a controller is to return a ``Response`` object:: use Symfony\Component\HttpFoundation\Response; @@ -645,29 +586,18 @@ headers and content that's sent back to the client:: $response = new Response(''); $response->headers->set('Content-Type', 'text/css'); -There are special classes that make certain kinds of responses easier: - -* For files, there is :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`. - See :ref:`component-http-foundation-serving-files`. - -* For streamed responses, there is - :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse`. - See :ref:`streaming-response`. - -.. seealso:: +There are special classes that make certain kinds of responses easier. Some of these +are mentioned below. To learn more about the ``Request`` and ``Response`` (and special +``Response`` classes), see the :ref:`HttpFoundation component documentation `. - Now that you know the basics you can continue your research on Symfony - ``Request`` and ``Response`` object in the - :ref:`HttpFoundation component documentation `. +Returning JSON Response +~~~~~~~~~~~~~~~~~~~~~~~ -JSON Helper -~~~~~~~~~~~ - -To return JSON from a controller, use the ``json()`` helper method on the base controller. -This returns a special ``JsonResponse`` object that encodes the data automatically:: +To return JSON from a controller, use the ``json()`` helper method. This returns a +special ``JsonResponse`` object that encodes the data automatically:: // ... - public function indexAction() + public function index() { // returns '{"username":"jane.doe"}' and sets the proper Content-Type header return $this->json(array('username' => 'jane.doe')); @@ -677,14 +607,11 @@ This returns a special ``JsonResponse`` object that encodes the data automatical } If the :doc:`serializer service ` is enabled in your -application, contents passed to ``json()`` are encoded with it. Otherwise, +application, it will be used to serialize the data to JSON. Otherwise, the :phpfunction:`json_encode` function is used. -File helper -~~~~~~~~~~~ - -.. versionadded:: 3.2 - The ``file()`` helper was introduced in Symfony 3.2. +Streaming File Responses +~~~~~~~~~~~~~~~~~~~~~~~~ You can use the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::file` helper to serve a file from inside a controller:: diff --git a/controller/argument_value_resolver.rst b/controller/argument_value_resolver.rst index 2772626e91f..811d226bc64 100644 --- a/controller/argument_value_resolver.rst +++ b/controller/argument_value_resolver.rst @@ -15,9 +15,6 @@ functionality. Functionality Shipped with the HttpKernel ----------------------------------------- -.. versionadded:: 3.3 - The ``SessionValueResolver`` and ``ServiceValueResolver`` were both added in Symfony 3.3. - Symfony ships with five value resolvers in the HttpKernel component: :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestAttributeValueResolver` @@ -45,11 +42,6 @@ Symfony ships with five value resolvers in the HttpKernel component: argument list. When the action is called, the last (variadic) argument will contain all the values of this array. -.. note:: - - Prior to Symfony 3.1, this logic was resolved within the ``ControllerResolver``. - The old functionality is rewritten to the aforementioned value resolvers. - Adding a Custom Value Resolver ------------------------------ @@ -58,14 +50,14 @@ definition. In the next example, you'll create a value resolver to inject the ``User`` object from the security system. Given you write the following controller:: - namespace AppBundle\Controller; + namespace App\Controller; - use AppBundle\Entity\User; + use App\Entity\User; use Symfony\Component\HttpFoundation\Response; class UserController { - public function indexAction(User $user) + public function index(User $user) { return new Response('Hello '.$user->getUsername().'!'); } @@ -92,10 +84,10 @@ Now that you know what to do, you can implement this interface. To get the current ``User``, you need the current security token. This token can be retrieved from the token storage:: - // src/AppBundle/ArgumentResolver/UserValueResolver.php - namespace AppBundle\ArgumentResolver; + // src/ArgumentResolver/UserValueResolver.php + namespace App\ArgumentResolver; - use AppBundle\Entity\User; + use App\Entity\User; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -148,20 +140,20 @@ and adding a priority. .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: _defaults: # ... be sure autowiring is enabled autowire: true # ... - AppBundle\ArgumentResolver\UserValueResolver: + App\ArgumentResolver\UserValueResolver: tags: - { name: controller.argument_value_resolver, priority: 50 } .. code-block:: xml - + - + @@ -181,8 +173,8 @@ and adding a priority. .. code-block:: php - // app/config/services.php - use AppBundle\ArgumentResolver\UserValueResolver; + // config/services.php + use App\ArgumentResolver\UserValueResolver; $container->autowire(UserValueResolver::class) ->addTag('controller.argument_value_resolver', array('priority' => 50)); diff --git a/controller/csrf_token_validation.rst b/controller/csrf_token_validation.rst deleted file mode 100644 index 8775362a63d..00000000000 --- a/controller/csrf_token_validation.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. index:: - single: Controller; Validating CSRF Tokens - -How to Manually Validate a CSRF Token in a Controller -===================================================== - -Sometimes, you want to use CSRF protection in an action where you do not -want to use the Symfony Form component. If, for example, you are implementing -a DELETE action, you can use the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isCsrfTokenValid` -method to check the validity of a CSRF token:: - - public function deleteAction() - { - if ($this->isCsrfTokenValid('token_id', $submittedToken)) { - // ... do something, like deleting an object - } - } diff --git a/controller/error_pages.rst b/controller/error_pages.rst index b2e45950e10..90e12577f71 100644 --- a/controller/error_pages.rst +++ b/controller/error_pages.rst @@ -67,33 +67,32 @@ logic to determine the template filename: To override these templates, simply rely on the standard Symfony method for :doc:`overriding templates that live inside a bundle `: -put them in the ``app/Resources/TwigBundle/views/Exception/`` directory. +put them in the ``templates/bundles/TwigBundle/Exception/`` directory. A typical project that returns HTML and JSON pages, might look like this: .. code-block:: text - app/ - └─ Resources/ + templates/ + └─ bundles/ └─ TwigBundle/ - └─ views/ - └─ Exception/ - ├─ error404.html.twig - ├─ error403.html.twig - ├─ error.html.twig # All other HTML errors (including 500) - ├─ error404.json.twig - ├─ error403.json.twig - └─ error.json.twig # All other JSON errors (including 500) + └─ Exception/ + ├─ error404.html.twig + ├─ error403.html.twig + ├─ error.html.twig # All other HTML errors (including 500) + ├─ error404.json.twig + ├─ error403.json.twig + └─ error.json.twig # All other JSON errors (including 500) Example 404 Error Template -------------------------- To override the 404 error template for HTML pages, create a new -``error404.html.twig`` template located at ``app/Resources/TwigBundle/views/Exception/``: +``error404.html.twig`` template located at ``templates/bundles/TwigBundle/Exception/``: .. code-block:: html+twig - {# app/Resources/TwigBundle/views/Exception/error404.html.twig #} + {# templates/bundles/TwigBundle/Exception/error404.html.twig #} {% extends 'base.html.twig' %} {% block body %} @@ -135,21 +134,22 @@ what it looks like and debug it? Fortunately, the default ``ExceptionController`` allows you to preview your *error* pages during development. -To use this feature, you need to have a definition in your -``routing_dev.yml`` file like so: +To use this feature, you need to load some special routes provided by TwigBundle +(if the application uses :doc:`Symfony Flex ` they are loaded +automatically when installing Twig support): .. configuration-block:: .. code-block:: yaml - # app/config/routing_dev.yml + # config/routes/dev/twig.yaml _errors: - resource: "@TwigBundle/Resources/config/routing/errors.xml" + resource: '@TwigBundle/Resources/config/routing/errors.xml' prefix: /_error .. code-block:: xml - + + - AppBundle:Exception:showException + App\Controller\ExceptionController::showException .. code-block:: php - // app/config/config.php + // config/packages/twig.php $container->loadFromExtension('twig', array( - 'exception_controller' => 'AppBundle:Exception:showException', + 'exception_controller' => 'App\Controller\ExceptionController::showException', // ... )); @@ -264,21 +258,21 @@ In that case, you might want to override one or both of the ``showAction()`` and .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: _defaults: # ... be sure autowiring is enabled autowire: true # ... - AppBundle\Controller\CustomExceptionController: + App\Controller\CustomExceptionController: public: true arguments: $debug: '%kernel.debug%' .. code-block:: xml - + - + %kernel.debug% @@ -299,8 +293,8 @@ In that case, you might want to override one or both of the ``showAction()`` and .. code-block:: php - // app/config/services.php - use AppBundle\Controller\CustomExceptionController; + // config/services.php + use App\Controller\CustomExceptionController; $container->autowire(CustomExceptionController::class) ->setArgument('$debug', '%kernel.debug%'); diff --git a/controller/forwarding.rst b/controller/forwarding.rst index df8a4c685b1..47ec70e9425 100644 --- a/controller/forwarding.rst +++ b/controller/forwarding.rst @@ -11,9 +11,9 @@ sub-request and calls the defined controller. The ``forward()`` method returns the :class:`Symfony\\Component\\HttpFoundation\\Response` object that is returned from *that* controller:: - public function indexAction($name) + public function index($name) { - $response = $this->forward('AppBundle:Something:fancy', array( + $response = $this->forward('App\Controller\OtherController::fancy', array( 'name' => $name, 'color' => 'green', )); @@ -26,10 +26,10 @@ from *that* controller:: The array passed to the method becomes the arguments for the resulting controller. The target controller method might look something like this:: - public function fancyAction($name, $color) + public function fancy($name, $color) { // ... create and return a Response object } Just like when creating a controller for a route, the order of the arguments -of ``fancyAction()`` doesn't matter: the matching is done by name. +of the ``fancy()`` method doesn't matter: the matching is done by name. diff --git a/controller/service.rst b/controller/service.rst index 2d86241a69e..8aceec8a690 100644 --- a/controller/service.rst +++ b/controller/service.rst @@ -5,50 +5,51 @@ How to Define Controllers as Services ===================================== In Symfony, a controller does *not* need to be registered as a service. But if you're -using the :ref:`default services.yml configuration `, +using the :ref:`default services.yaml configuration `, your controllers *are* already registered as services. This means you can use dependency injection like any other normal service. Referencing your Service from Routing ------------------------------------- -Registering your controller as a service is great, but you also need to make sure -that your routing references the service properly, so that Symfony knows to use it. +Registering your controller as a service is the first step, but you also need to +update your routing config to reference the service properly, so that Symfony +knows to use it. -If the service id is the fully-qualified class name (FQCN) of your controller, you're -done! You can use the normal ``AppBundle:Hello:index`` syntax in your routing and -it will find your service. - -But, if your service has a different id, you can use a special ``SERVICEID:METHOD`` -syntax: +Use the ``service_id::method_name`` syntax to refer to the controller method. +If the service id is the fully-qualified class name (FQCN) of your controller, +as Symfony recommends, then the syntax is the same as if the controller was not +a service like: ``App\Controller\HelloController::index``: .. configuration-block:: .. code-block:: php-annotations - // src/AppBundle/Controller/HelloController.php + // src/Controller/HelloController.php - // You need to use Sensio's annotation to specify a service id - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Symfony\Component\Routing\Annotation\Route; - /** - * @Route(service="app.hello_controller") - */ class HelloController { - // ... + /** + * @Route("/hello", name="hello") + */ + public function index() + { + // ... + } } .. code-block:: yaml - # app/config/routing.yml + # config/routes.yaml hello: path: /hello - defaults: { _controller: app.hello_controller:indexAction } + defaults: { _controller: App\Controller\HelloController::index } .. code-block:: xml - + - app.hello_controller:indexAction + App\Controller\HelloController::index .. code-block:: php - // app/config/routing.php + // config/routes.php $collection->add('hello', new Route('/hello', array( - '_controller' => 'app.hello_controller:indexAction', + '_controller' => 'App\Controller\HelloController::index', ))); -.. note:: - - You cannot drop the ``Action`` part of the method name when using the - single colon notation. - .. _controller-service-invoke: Invokable Controllers @@ -80,7 +76,7 @@ Invokable Controllers If your controller implements the ``__invoke()`` method - popular with the Action-Domain-Response (ADR) pattern, you can simply refer to the service id -(``AppBundle\Controller\HelloController`` or ``app.hello_controller`` for example). +without the method (``App\Controller\HelloController`` for example). Alternatives to base Controller Methods --------------------------------------- @@ -94,11 +90,11 @@ The base `Controller class source code`_ is a great way to see how to accomplish common tasks. For example, ``$this->render()`` is usually used to render a Twig template and return a Response. But, you can also do this directly: -In a controller that's defined as a service, you can instead inject the ``templating`` +In a controller that's defined as a service, you can instead inject the ``twig`` service and use it directly:: - // src/AppBundle/Controller/HelloController.php - namespace AppBundle\Controller; + // src/Controller/HelloController.php + namespace App\Controller; use Symfony\Component\HttpFoundation\Response; @@ -111,7 +107,7 @@ service and use it directly:: $this->twig = $twig; } - public function indexAction($name) + public function index($name) { $content = $this->twig->render( 'hello/index.html.twig', diff --git a/controller/soap_web_service.rst b/controller/soap_web_service.rst index ec54c5a9f0d..d41576cb414 100644 --- a/controller/soap_web_service.rst +++ b/controller/soap_web_service.rst @@ -24,8 +24,8 @@ which represents the functionality that you'll expose in your SOAP service. In this case, the SOAP service will allow the client to call a method called ``hello``, which happens to send an email:: - // src/AppBundle/Service/HelloService.php - namespace AppBundle\Service; + // src/Service/HelloService.php + namespace App\Service; class HelloService { @@ -54,22 +54,22 @@ the :ref:`default services configuration setObject($helloService); @@ -96,10 +96,10 @@ into the content of the Response and clear the output buffer. Finally, you're ready to return the ``Response``. Below is an example calling the service using a `NuSOAP`_ client. This example -assumes that the ``indexAction()`` in the controller above is accessible via the -route ``/soap``:: +assumes that the ``index()`` method in the controller above is accessible via +the route ``/soap``:: - $soapClient = new \SoapClient('http://example.com/app.php/soap?wsdl'); + $soapClient = new \SoapClient('http://example.com/index.php/soap?wsdl'); $result = $soapClient->call('hello', array('name' => 'Scott')); @@ -160,7 +160,7 @@ An example WSDL is below. - + diff --git a/controller/upload_file.rst b/controller/upload_file.rst index d217811845b..0540b0abb9f 100644 --- a/controller/upload_file.rst +++ b/controller/upload_file.rst @@ -15,8 +15,8 @@ Imagine that you have a ``Product`` entity in your application and you want to add a PDF brochure for each product. To do so, add a new property called ``brochure`` in the ``Product`` entity:: - // src/AppBundle/Entity/Product.php - namespace AppBundle\Entity; + // src/Entity/Product.php + namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -51,10 +51,10 @@ or ``blob`` because it just stores the PDF file name instead of the file content Then, add a new ``brochure`` field to the form that manages the ``Product`` entity:: - // src/AppBundle/Form/ProductType.php - namespace AppBundle\Form; + // src/Form/ProductType.php + namespace App\Form; - use AppBundle\Entity\Product; + use App\Entity\Product; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -87,7 +87,7 @@ to :doc:`customize form rendering `): .. code-block:: html+twig - {# app/Resources/views/product/new.html.twig #} + {# templates/product/new.html.twig #}

Adding a new product

{{ form_start(form) }} @@ -98,7 +98,7 @@ to :doc:`customize form rendering `): .. code-block:: html+php - +

Adding a new product

start($form) ?> @@ -107,21 +107,21 @@ to :doc:`customize form rendering `): Finally, you need to update the code of the controller that handles the form:: - // src/AppBundle/Controller/ProductController.php - namespace AppBundle\Controller; + // src/Controller/ProductController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; - use AppBundle\Entity\Product; - use AppBundle\Form\ProductType; + use App\Entity\Product; + use App\Form\ProductType; class ProductController extends Controller { /** * @Route("/product/new", name="app_product_new") */ - public function newAction(Request $request) + public function new(Request $request) { $product = new Product(); $form = $this->createForm(ProductType::class, $product); @@ -170,11 +170,11 @@ controller to specify the directory in which the brochures should be stored: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml # ... parameters: - brochures_directory: '%kernel.project_dir%/web/uploads/brochures' + brochures_directory: '%kernel.project_dir%/public/uploads/brochures' There are some important things to consider in the code of the above controller: @@ -195,6 +195,11 @@ There are some important things to consider in the code of the above controller: use the :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::guessExtension` method to let Symfony guess the right extension according to the file MIME type; +.. versionadded:: 4.1 + The :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientSize` + method was deprecated in Symfony 4.1 and will be removed in Symfony 5.0. + Use ``getSize()`` instead. + You can use the following code to link to the PDF brochure of a product: .. configuration-block:: @@ -230,8 +235,8 @@ Creating an Uploader Service To avoid logic in controllers, making them big, you can extract the upload logic to a separate service:: - // src/AppBundle/Service/FileUploader.php - namespace AppBundle\Service; + // src/Service/FileUploader.php + namespace App\Service; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -265,17 +270,17 @@ Then, define a service for this class: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - AppBundle\Service\FileUploader: + App\Service\FileUploader: arguments: $targetDirectory: '%brochures_directory%' .. code-block:: xml - + - + %brochures_directory% .. code-block:: php - // app/config/services.php - use AppBundle\Service\FileUploader; + // config/services.php + use App\Service\FileUploader; $container->autowire(FileUploader::class) ->setArgument('$targetDirectory', '%brochures_directory%'); Now you're ready to use this service in the controller:: - // src/AppBundle/Controller/ProductController.php + // src/Controller/ProductController.php use Symfony\Component\HttpFoundation\Request; - use AppBundle\Service\FileUploader; + use App\Service\FileUploader; // ... - public function newAction(Request $request, FileUploader $fileUploader) + public function new(Request $request, FileUploader $fileUploader) { // ... @@ -326,14 +331,14 @@ If you are using Doctrine to store the Product entity, you can create a :doc:`Doctrine listener ` to automatically upload the file when persisting the entity:: - // src/AppBundle/EventListener/BrochureUploadListener.php - namespace AppBundle\EventListener; + // src/EventListener/BrochureUploadListener.php + namespace App\EventListener; use Symfony\Component\HttpFoundation\File\UploadedFile; use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; - use AppBundle\Entity\Product; - use AppBundle\Service\FileUploader; + use App\Entity\Product; + use App\Service\FileUploader; class BrochureUploadListener { @@ -381,41 +386,43 @@ Now, register this class as a Doctrine listener: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: _defaults: # ... be sure autowiring is enabled autowire: true # ... - AppBundle\EventListener\BrochureUploadListener: + App\EventListener\BrochureUploadListener: tags: - { name: doctrine.event_listener, event: prePersist } - { name: doctrine.event_listener, event: preUpdate } .. code-block:: xml - + - - - + + + + - - - - + + + + + .. code-block:: php - // app/config/services.php - use AppBundle\EventListener\BrochureUploaderListener; + // config/services.php + use App\EventListener\BrochureUploaderListener; $container->autowire(BrochureUploaderListener::class) ->addTag('doctrine.event_listener', array( diff --git a/create_framework/http_kernel_controller_resolver.rst b/create_framework/http_kernel_controller_resolver.rst index b1b8bdaee17..ff1408355ea 100644 --- a/create_framework/http_kernel_controller_resolver.rst +++ b/create_framework/http_kernel_controller_resolver.rst @@ -53,19 +53,9 @@ based on a Request object. All controller resolvers implement the following inte // ... interface ControllerResolverInterface { - function getController(Request $request); - - function getArguments(Request $request, $controller); + public function getController(Request $request); } -.. caution:: - - The ``getArguments()`` method is deprecated as of Symfony 3.1. and will be - removed in 4.0. You can use the - :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver` which - uses the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface` - instead. - The ``getController()`` method relies on the same convention as the one we have defined earlier: the ``_controller`` request attribute must contain the controller associated with the Request. Besides the built-in PHP callbacks, @@ -98,7 +88,16 @@ resolver from HttpKernel:: Now, let's see how the controller arguments are guessed. ``getArguments()`` introspects the controller signature to determine which arguments to pass to -it by using the native PHP `reflection`_. +it by using the native PHP `reflection`_. This method is defined in the +following interface:: + + namespace Symfony\Component\HttpKernel\Controller; + + // ... + interface ArgumentResolverInterface + { + public function getArguments(Request $request, $controller); + } The ``indexAction()`` method needs the Request object as an argument. ``getArguments()`` knows when to inject it properly if it is type-hinted diff --git a/debug.rst b/debug.rst deleted file mode 100644 index 49d3074b799..00000000000 --- a/debug.rst +++ /dev/null @@ -1,8 +0,0 @@ -Debugging -========= - -.. toctree:: - :maxdepth: 1 - :glob: - - debug/* diff --git a/debug/debugging.rst b/debug/debugging.rst deleted file mode 100644 index 386c472cc10..00000000000 --- a/debug/debugging.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. index:: - single: Debugging - -How to Optimize your Development Environment for Debugging -========================================================== - -When you work on a Symfony project on your local machine, you should use the -``dev`` environment (``app_dev.php`` front controller). This environment -configuration is optimized for two main purposes: - -* Give the developer accurate feedback whenever something goes wrong (web - debug toolbar, nice exception pages, profiler, ...); - -* Be as similar as possible as the production environment to avoid problems - when deploying the project. - -Disabling the Bootstrap File and Class Caching ----------------------------------------------- - -And to make the production environment as fast as possible, Symfony creates -big PHP files in your cache containing the aggregation of PHP classes your -project needs for every request. However, this behavior can confuse your debugger, -because the same class can be located in two different places: the original class -file and the big file which aggregates lots of classes. - -This recipe shows you how you can tweak this caching mechanism to make it friendlier -when you need to debug code that involves Symfony classes. - -The ``app_dev.php`` front controller reads as follows by default:: - - // ... - - $loader = require __DIR__.'/../app/autoload.php'; - Debug::enable(); - - $kernel = new AppKernel('dev', true); - $kernel->loadClassCache(); - $request = Request::createFromGlobals(); - // ... - -To make your debugger happier, disable the loading of all PHP class caches -by removing the call to ``loadClassCache()``:: - - // ... - - $loader = require_once __DIR__.'/../app/autoload.php'; - Debug::enable(); - - $kernel = new AppKernel('dev', true); - // $kernel->loadClassCache(); - $request = Request::createFromGlobals(); - -.. tip:: - - If you disable the PHP caches, don't forget to revert after your debugging - session. - -Some IDEs do not like the fact that some classes are stored in different -locations. To avoid problems, you can either tell your IDE to ignore the PHP -cache files, or you can change the extension used by Symfony for these files:: - - $kernel->loadClassCache('classes', '.php.cache'); - -.. versionadded:: 3.3 - The ``loadClassCache()`` was deprecated in Symfony 3.3 and removed in - Symfony 4.0. No alternative is provided because this feature is useless - when using PHP 7 or higher. - -Useful Debugging Commands -------------------------- - -When developing a large application, it can be hard to keep track of all the -different services, routes and translations. Luckily, Symfony has some commands -that can help you visualize and find the information. - -``about`` - Shows information about the current project, such as the Symfony version, - the Kernel and PHP. - -``debug:container`` - Displays information about the contents of the Symfony container for all public - services. To find only those matching a name, append the name as an argument. - -``debug:config`` - Shows all configured bundles, their class and their alias. - -``debug:form`` - Displays information about form types and their options. - -``debug:event-dispatcher`` - Displays information about all the registered listeners in the event dispatcher. - -``debug:router`` - Displays information about all configured routes in the application as a - table with the name, method, scheme, host and path for each route. - -``debug:translation `` - Shows a table of the translation key, the domain, the translation and the - fallback translation for all known messages, if translations exist for - the given locale. - -.. tip:: - - When in doubt how to use a console command, open the help section by - appending the ``--help`` option. diff --git a/deployment.rst b/deployment.rst index 6d11a7a014a..34ca62782bb 100644 --- a/deployment.rst +++ b/deployment.rst @@ -30,7 +30,7 @@ A deployment may also include other tasks, such as: repository; * Creating a temporary staging area to build your updated setup "offline"; * Running any tests available to ensure code and/or server stability; -* Removal of any unnecessary files from the ``web/`` directory to keep your +* Removal of any unnecessary files from the ``public/`` directory to keep your production environment clean; * Clearing of external cache systems (like `Memcached`_ or `Redis`_). @@ -63,14 +63,13 @@ manually taking other steps (see `Common Post-Deployment Tasks`_). Using Platforms as a Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The specific deployment steps vary greatly from one service provider to another, -so check out the dedicated article for the service of your choose: +Using a Platform as a Service (PaaS) can be a great way to deploy your Symfony app +quickly and easily. There are many PaaS - below are a few that work well with Symfony: -.. toctree:: - :maxdepth: 1 - :glob: - - deployment/* +* `Heroku`_ +* `Platform.sh`_ +* `Azure`_ +* `fortrabbit`_ Using Build Scripts and other Tools ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -125,18 +124,16 @@ Check if your server meets the requirements by running: .. _b-configure-your-app-config-parameters-yml-file: -B) Configure your Parameters File -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +B) Configure your Environment Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Most Symfony applications define configuration parameters in a file called -``app/config/parameters.yml``. This file should *not* be deployed, because -Symfony generates it automatically using the ``app/config/parameters.yml.dist`` -file as a template (that's why ``parameters.yml.dist`` must be committed and -deployed). +Most Symfony applications read their configuration from environment variables. +While developing locally, you'll usually store these in a ``.env`` file. But on +production, instead of creating this file, you should set *real* environment variables. -If your application uses environment variables instead of these parameters, you -must define those env vars in your production server using the tools provided by -your hosting service. +How you set environment variables, depends on your setup: they can be set at the +command line, in your Nginx configuration, or via other methods provided by your +hosting service. At the very least you need to define the ``SYMFONY_ENV=prod`` (or ``APP_ENV=prod`` if you're using :doc:`Symfony Flex `) to run the @@ -232,9 +229,9 @@ If your deployment method doesn't use Composer, you may have removed the The solution is to override the ``getProjectDir()`` method in the application kernel and return your project's root directory:: - // app/AppKernel.php + // src/Kernel.php // ... - class AppKernel extends Kernel + class Kernel extends BaseKernel { // ... @@ -244,6 +241,14 @@ kernel and return your project's root directory:: } } +Learn More +---------- + +.. toctree:: + :maxdepth: 1 + + deployment/proxies + .. _`Capifony`: https://github.com/everzet/capifony .. _`Capistrano`: http://capistranorb.com/ .. _`sf2debpkg`: https://github.com/liip/sf2debpkg @@ -256,4 +261,8 @@ kernel and return your project's root directory:: .. _`Symfony plugin`: https://github.com/capistrano/symfony/ .. _`Deployer`: http://deployer.org/ .. _`Git Tagging`: https://git-scm.com/book/en/v2/Git-Basics-Tagging +.. _`Heroku`: https://devcenter.heroku.com/articles/getting-started-with-symfony +.. _`platform.sh`: https://docs.platform.sh/frameworks/symfony.html +.. _`Azure`: https://azure.microsoft.com/en-us/develop/php/ +.. _`fortrabbit`: https://help.fortrabbit.com/install-symfony .. _`EasyDeployBundle`: https://github.com/EasyCorp/easy-deploy-bundle diff --git a/deployment/azure-website.rst b/deployment/azure-website.rst index 05626ae03f2..15361b9e416 100644 --- a/deployment/azure-website.rst +++ b/deployment/azure-website.rst @@ -1,458 +1,12 @@ +:orphan: + .. index:: single: Deployment; Deploying to Microsoft Azure Website Cloud -Deploying to Microsoft Azure Website Cloud -========================================== - -This step by step article describes how to deploy a small Symfony web -application to the Microsoft Azure Website cloud platform. It will explain how -to set up a new Azure website including configuring the right PHP version and -global environment variables. The document also shows how you can leverage -Git and Composer to deploy your Symfony application to the cloud. - -Setting up the Azure Website ----------------------------- - -To set up a new Microsoft Azure Website, first `sign up with Azure`_ or sign in -with your credentials. Once you're connected to your `Azure Portal`_ interface, -select the **New** panel. On this panel, use the search bar, search for -**Web App + MySQL** and choose **Web App + MySQL** by **Microsoft** and -click **Create**: - -.. image:: /_images/deployment/azure-website/step-01.png - :alt: Create a new custom Azure Website - -Step 1: Create Web Site -~~~~~~~~~~~~~~~~~~~~~~~ - -Here, you will be prompted to fill in some basic information. - -.. image:: /_images/deployment/azure-website/step-02.png - :alt: Setup the Azure Website - -For the URL, enter the URL that you would like to use for your Symfony -application, then select your **Subscription**, **Create a new Resource Group** -(which is a collection of resources that share the same lifecycle, permissions -and policies). Pick ClearDB as a **Database Provider**. Create a new **App -Service plan/Location** you will be prompted to set up your app service plan -with a name, a region and a pricing tier. Then create a new **Database**, you -will be prompted to set up your MySQL database storage with a database name and -a region. The MySQL database storage is provided by Microsoft in partnership -with ClearDB. Choose the same region you selected for App Service plan. - -Click Create to continue. - -Once you created the web site, select **All resources** in the left menu and -choose the website you just created. - -Step 2: Where Is your Source Code -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now, select **Deployment options** under **APP DEPLOYMENT**, select **Choose -Source** and choose **Local Git repository** to configure your Azure Website -credentials. If you choose a different source like GitHub or Bitbucket you can -ignore the next step. - -.. image:: /_images/deployment/azure-website/step-03.png - :alt: Setup a local Git repository - -Once you selected **Local Git repository**, click **Setup connection** you will -be prompted to create a username and a secure password: these will become -essential identifiers to connect to the FTP server and also to push your -application code to the Git repository. - -.. image:: /_images/deployment/azure-website/step-04.png - :alt: Configure Azure Website credentials - -Congratulations! Your Azure Website is now up and running. You can check -it by browsing to the Website url you configured in the first step. You should -see the following display in your web browser: - -.. image:: /_images/deployment/azure-website/step-05.png - :alt: Azure Website is running - -The Microsoft Azure portal also provides a complete control panel for the Azure -Website. - -.. image:: /_images/deployment/azure-website/step-06.png - :alt: Azure Website Control Panel - -Your Azure Website is ready! But to run a Symfony site, you need to configure -just a few additional things. - -Configuring the Azure Website for Symfony ------------------------------------------ - -This section of the tutorial details how to configure the correct version of PHP -to run Symfony. It also shows you how to enable some mandatory PHP extensions -and how to properly configure PHP for a production environment. - -Configuring the latest PHP Runtime -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Even though Symfony only requires PHP 5.5.9 to run, it's always recommended -to use the most recent PHP version whenever possible. Earlier versions are no longer -supported by the PHP core team, but you can update it easily in Azure. - -To update your PHP version on Azure, go to the **Application settings** under -**SETTINGS** and select the version you want. - -.. image:: /_images/deployment/azure-website/step-07.png - :alt: Enabling the most recent PHP runtime from Azure Website Control Panel - -Click the **Save** button in the bottom bar to save your changes and restart -the web server. - -.. note:: - - Choosing a more recent PHP version can greatly improve runtime performance. - PHP 5.5 ships with a new built-in PHP accelerator called OPCache that - replaces APC. On an Azure Website, OPCache is already enabled and there - is no need to install and set up APC. - - The following screenshot shows the output of a :phpfunction:`phpinfo` script - run from an Azure Website to verify that PHP 7.0 is running with - OPCache enabled. - - .. image:: /_images/deployment/azure-website/step-08.png - :alt: OPCache Configuration - -Tweaking php.ini Configuration Settings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Microsoft Azure allows you to override the ``php.ini`` global configuration -settings by creating a custom ``.user.ini`` file under the project root -directory (``site/wwwroot``). - -.. code-block:: ini - - ; .user.ini - expose_php = Off - memory_limit = 256M - upload_max_filesize = 10M - -None of these settings *needs* to be overridden. The default PHP configuration -is already pretty good, so this is just an example to show how you can easily -tweak PHP internal settings by uploading your custom ``.ini`` file. - -You can either manually create this file on your Azure Website FTP server under -the ``site/wwwroot`` directory or deploy it with Git. You can get your FTP -server credentials from the Azure Website Control panel under the **Dashboard** -tab on the right sidebar. If you want to use Git, simply put your ``.user.ini`` -file at the root of your local repository and push your commits to your Azure -Website repository. - -.. note:: - - `Deploying from Git`_ is dedicated to explaining how to configure your - Azure Website Git repository and how to push the commits to be deployed. - You can also learn more about configuring PHP internal settings on the - official `PHP MSDN documentation`_ page. - -Enabling the PHP intl Extension -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**The** ``intl`` **extension is now enabled by default. The following steps are -no longer necessary.** You can check if the ``intl`` extension is enabled in the -:phpfunction:`phpinfo` page. - -However if the ``intl`` extension is not enabled you can follow these steps. - -This is the tricky part of the guide! To enable the ``intl`` extension, there is -no need to upload any DLL files as the ``php_intl.dll`` file already exists on -Azure. In fact, this file just needs to be moved into the custom website -extension directory. - -To get the ``php_intl.dll`` file under your ``site/wwwroot`` directory, simply -access the online **Kudu** tool by browsing to the following URL: - -.. code-block:: text - - https://[your-website-name].scm.azurewebsites.net - -**Kudu** is a set of tools to manage your application. It comes with a file -explorer, a command line prompt, a log stream and a configuration settings summary -page. Of course, this section can only be accessed if you're logged in to -your main Azure Website account. - -.. image:: /_images/deployment/azure-website/step-09.png - :alt: The Kudu Panel - -From the Kudu front page, click on the **Debug Console** navigation item in the -main menu and choose **CMD**. This should open the **Debug Console** page -that shows a file explorer and a console prompt below. - -In the console prompt, type the following three commands to copy the original -``php_intl.dll`` extension file into a custom website ``ext/`` directory. This -new directory must be created under the main directory ``site/wwwroot``. - -.. code-block:: terminal - - $ cd site\wwwroot - $ mkdir ext - $ copy "D:\Program Files (x86)\PHP\v5.5\ext\php_intl.dll" ext - -The whole process and output should look like this: - -.. image:: /_images/deployment/azure-website/step-10.png - :alt: Executing commands in the online Kudu Console prompt - -To complete the activation of the ``php_intl.dll`` extension, you must tell -Azure Website to load it from the newly created ``ext`` directory. This can be -done by registering a global ``PHP_EXTENSIONS`` environment variable from -the **Application settings** page of the main Azure Website control panel. - -In the **app settings** section, register the ``PHP_EXTENSIONS`` environment -variable with the value ``ext\php_intl.dll`` as shown in the screenshot below: - -.. image:: /_images/deployment/azure-website/step-11.png - :alt: Registering custom PHP extensions - -Hit "save" to confirm your changes and restart the web server. The PHP ``Intl`` -extension should now be available in your web server environment. The following -screenshot of a :phpfunction:`phpinfo` page verifies the ``intl`` extension is -properly enabled: - -.. image:: /_images/deployment/azure-website/step-12.png - :alt: Intl extension is enabled - -Great! The PHP environment setup is now complete. Next, you'll learn how -to configure the Git repository and push code to production. You'll also -learn how to install and configure the Symfony app after it's deployed. - -Deploying from Git -~~~~~~~~~~~~~~~~~~ - -First, make sure Git is correctly installed on your local machine using the -following command in your terminal: - -.. code-block:: terminal - - $ git --version - -.. note:: - - Get your Git from the `git-scm.com`_ website and follow the instructions - to install and configure it on your local machine. - -In the Azure Website Control panel, browse the **Overview** tab to get the -Git repository URL where you should push your code: - -.. image:: /_images/deployment/azure-website/step-13.png - :alt: Git deployment panel - -Now, you'll want to connect your local Symfony application with this remote -Git repository on Azure Website. If your Symfony application is not yet stored -with Git, you must first create a Git repository in your Symfony application -directory with the ``git init`` command and commit to it with the ``git commit`` -command. - -Also, make sure your Symfony repository has a ``.gitignore`` file at its root -directory with at least the following contents: - -.. code-block:: text - - /var/bootstrap.php.cache - /var/cache/* - /app/config/parameters.yml - /var/logs/* - !var/cache/.gitkeep - !var/logs/.gitkeep - /var/SymfonyRequirements.php - /build/ - /vendor/ - /bin/ - /composer.phar - /web/app_dev.php - /web/bundles/ - /web/config.php - -The ``.gitignore`` file asks Git not to track any of the files and directories -that match these patterns. This means these files won't be deployed to the Azure -Website. - -Now, from the command line on your local machine, type the following at the -root of your Symfony project: - -.. code-block:: terminal - - $ git remote add azure https://@.scm.azurewebsites.net:443/.git - $ git push azure master - -Don't forget to replace the values enclosed by ``<`` and ``>`` with your custom -settings displayed in the **Deployment** tab of your Azure Website panel. The -``git remote`` command connects the Azure Website remote Git repository and -assigns an alias to it with the name ``azure``. The second ``git push`` command -pushes all your commits to the remote ``master`` branch of your remote ``azure`` -Git repository. - -The deployment with Git should produce an output similar to the screenshot -below: - -.. image:: /_images/deployment/azure-website/step-14.png - :alt: Deploying files to the Git Azure Website repository - -The code of the Symfony application has now been deployed to the Azure Website -which you can browse from the file explorer of the Kudu application. You should -see the ``app/``, ``src/`` and ``web/`` directories under your ``site/wwwroot`` -directory on the Azure Website filesystem. - -Configure the Symfony Application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PHP has been configured and your code has been pushed with Git. The last -step is to configure the application and install the third party dependencies -it requires that aren't tracked by Git. Switch back to the online **Console** -of the Kudu application and execute the following commands in it: - -.. code-block:: terminal - - $ cd site\wwwroot - $ curl -sS https://getcomposer.org/installer | php - $ php composer.phar install - -The ``curl`` command retrieves and downloads the Composer command line tool and -installs it at the root of the ``site/wwwroot`` directory. Then, running -the Composer ``install`` command downloads and installs all necessary third-party -libraries. - -This may take a while depending on the number of third-party dependencies -you've configured in your ``composer.json`` file. - -At the end of the ``composer install`` command, you will be prompted to fill in -the values of some Symfony settings like database credentials, locale, mailer -credentials, CSRF token protection, etc. These parameters come from the -``app/config/parameters.yml.dist`` file. - -.. image:: /_images/deployment/azure-website/step-15.png - :alt: Configuring Symfony global parameters - -The most important thing in this article is to correctly set up your database -settings. You can get your MySQL database settings in the **Application -settings** page. Simply click on the **Show connection string values** link to -make them appear. - -.. image:: /_images/deployment/azure-website/step-16.png - :alt: MySQL database settings - -The displayed MySQL database settings should be something similar to the code -below. Of course, each value depends on what you've already configured. - -.. code-block:: text - - Database=mysymfonyMySQL;Data Source=eu-cdbr-azure-north-c.cloudapp.net;User Id=bff2481a5b6074;Password=bdf50b42 - -Switch back to the console and answer the prompted questions and provide the -following answers. Don't forget to adapt the values below with your real values -from the MySQL connection string. - -.. code-block:: text - - database_driver: pdo_mysql - database_host: u-cdbr-azure-north-c.cloudapp.net - database_port: null - database_name: mysymfonyMySQL - database_user: bff2481a5b6074 - database_password: bdf50b42 - // ... - -Don't forget to answer all the questions. It's important to set a unique random -string for the ``secret`` variable. For the mailer configuration, Azure Website -doesn't provide a built-in mailer service. You should consider configuring -the host-name and credentials of some other third-party mailing service if -your application needs to send emails. - -Your Symfony application is now configured and should be almost operational. The -final step is to build the database schema. This can easily be done with the -command line interface if you're using Doctrine. In the online **Console** tool -of the Kudu application, run the following command to mount the tables into your -MySQL database. - -.. code-block:: terminal - - $ php bin/console doctrine:schema:update --force - -This command builds the tables and indexes for your MySQL database. If your -Symfony application is more complex than a basic Symfony Standard Edition, you -may have additional commands to execute for setup (see :doc:`/deployment`). - -Make sure that your application is running by browsing the ``app.php`` front -controller with your web browser and the following URL: - -.. code-block:: terminal - - http://.azurewebsites.net/web/app.php - -If Symfony is correctly installed, you should see the front page of your Symfony -application showing. - -Configure the Web Server -~~~~~~~~~~~~~~~~~~~~~~~~ - -At this point, the Symfony application has been deployed and works perfectly on -the Azure Website. However, the ``web`` folder is still part of the URL, which -you definitely don't want. But don't worry! You can easily configure the web -server to point to the ``web`` folder and remove the ``web`` in the URL (and -guarantee that nobody can access files outside of the ``web`` directory.) - -To do this, create and deploy (see previous section about Git) the following -``web.config`` file. This file must be located at the root of your project -next to the ``composer.json`` file. This file is the Microsoft IIS Server -equivalent to the well-known ``.htaccess`` file from Apache. For a Symfony -application, configure it with the following content: - -.. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -As you can see, the latest rule ``RewriteRequestsToPublic`` is responsible for -rewriting any URLs to the ``web/app.php`` front controller which allows you to -skip the ``web/`` folder in the URL. The first rule called ``BlockAccessToPublic`` -matches all URL patterns that contain the ``web/`` folder and serves a -``403 Forbidden`` HTTP response instead. This example is based on Benjamin -Eberlei's sample you can find on GitHub in the `SymfonyAzureEdition`_ bundle. - -Deploy this file under the ``site/wwwroot`` directory of the Azure Website and -browse to your application without the ``web/app.php`` segment in the URL. - -Conclusion ----------- +Deploying to Microsoft Azure +============================ -Nice work! You've now deployed your Symfony application to the Microsoft -Azure Website Cloud platform. You also saw that Symfony can be easily configured -and executed on a Microsoft IIS web server. The process is simple and easy -to implement. And as a bonus, Microsoft is continuing to reduce the number -of steps needed so that deployment becomes even easier. +If you want information about deploying to Azure, see their official documentation: +`Create your PHP web application on Azure`_ -.. _`sign up with Azure`: https://azure.microsoft.com/free/ -.. _`Azure Portal`: https://portal.azure.com -.. _`PHP MSDN documentation`: https://blogs.msdn.com/b/silverlining/archive/2012/07/10/configuring-php-in-windows-azure-websites-with-user-ini-files.aspx -.. _`git-scm.com`: https://git-scm.com/download -.. _`SymfonyAzureEdition`: https://github.com/beberlei/symfony-azure-edition/ +.. _`Create your PHP web application on Azure`: https://azure.microsoft.com/en-us/develop/php/ diff --git a/deployment/fortrabbit.rst b/deployment/fortrabbit.rst index ffe968e813d..f13bf4f0ad4 100644 --- a/deployment/fortrabbit.rst +++ b/deployment/fortrabbit.rst @@ -1,284 +1,12 @@ +:orphan: + .. index:: single: Deployment; Deploying to fortrabbit.com Deploying to fortrabbit ======================= -This step-by-step article describes how to deploy a Symfony web application to -`fortrabbit`_. You can read more about using Symfony with fortrabbit on the -official fortrabbit `Symfony install guide`_. - -Setting up fortrabbit ---------------------- - -Before getting started, you should have done a few things on the fortrabbit side: - -* `Sign up`_; -* Add an SSH key to your Account (to deploy via Git); -* Create an App. - -Preparing your Application --------------------------- - -You don't need to change any code to deploy a Symfony application to fortrabbit. -But it requires some minor tweaks to its configuration. - -Configure Logging -~~~~~~~~~~~~~~~~~ - -Per default Symfony logs to a file. Modify the ``app/config/config_prod.yml`` file -to redirect it to :phpfunction:`error_log`: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config_prod.yml - monolog: - # ... - handlers: - nested: - type: error_log - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/config_prod.php - $container->loadFromExtension('monolog', array( - // ... - 'handlers' => array( - 'nested' => array( - 'type' => 'error_log', - ), - ), - )); - -Configuring Database Access & Session Handler -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can use the fortrabbit App Secrets to attain your database credentials. -Create the file ``app/config/config_prod_secrets.php`` with the following -contents:: - - // get the path to the secrects.json file - $secrets = getenv("APP_SECRETS") - if (!$secrets) { - return; - } - - // read the file and decode json to an array - $secrets = json_decode(file_get_contents($secrets), true); - - // set database parameters to the container - if (isset($secrets['MYSQL'])) { - $container->setParameter('database_driver', 'pdo_mysql'); - $container->setParameter('database_host', $secrets['MYSQL']['HOST']); - $container->setParameter('database_name', $secrets['MYSQL']['DATABASE']); - $container->setParameter('database_user', $secrets['MYSQL']['USER']); - $container->setParameter('database_password', $secrets['MYSQL']['PASSWORD']); - } - - // check if the Memcache component is present - if (isset($secrets['MEMCACHE'])) { - $memcache = $secrets['MEMCACHE']; - $handlers = array(); - - foreach (range(1, $memcache['COUNT']) as $num) { - $handlers[] = $memcache['HOST'.$num].':'.$memcache['PORT'.$num]; - } - - // apply ini settings - ini_set('session.save_handler', 'memcached'); - ini_set('session.save_path', implode(',', $handlers)); - - if ("2" === $memcache['COUNT']) { - ini_set('memcached.sess_number_of_replicas', 1); - ini_set('memcached.sess_consistent_hash', 1); - ini_set('memcached.sess_binary', 1); - } - } - -Make sure this file is imported into the main config file: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config_prod.yml - imports: - - { resource: config.yml } - - { resource: config_prod_secrets.php } - - # .. - framework: - session: - # set handler_id to null to use default session handler from php.ini (memcached) - handler_id: ~ - # .. - - .. code-block:: xml - - - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/config_prod.php - $loader->import('config.php'); - $loader->import('config_prod_secrets.php'); - - $container->loadFromExtension('framework', array( - 'session' => array( - 'handler_id' => null, - ), - )); - - // ... - -Configuring the Environment in the Dashboard --------------------------------------------- - -PHP Settings -~~~~~~~~~~~~ - -The PHP version and enabled extensions are configurable under the PHP settings -of your App within the fortrabbit Dashboard. - -Environment Variables -~~~~~~~~~~~~~~~~~~~~~ - -Set the ``SYMFONY_ENV`` environment variable to ``prod`` to make sure the right -config files get loaded. ENV vars are configuable in fortrabbit Dashboard as well. - -Document Root -~~~~~~~~~~~~~ - -The document root is configurable for every custom domain you setup for your App. -The default is ``/htdocs``, but for Symfony you probably want to change it to -``/htdocs/web``. You also do so in the fortrabbit Dashboard under ``Domain`` settings. - -Deploying to fortrabbit ------------------------ - -It is assumed that your codebase is under version-control with Git and dependencies -are managed with Composer (locally). - -Every time you push to fortrabbit composer install runs before your code gets -deployed. To finetune the deployment behavior put a `fortrabbit.yml`_. deployment -file (optional) in the project root. - -Add fortrabbit as a (additional) Git remote and add your configuration changes: - -.. code-block:: terminal - - $ git remote add fortrabbit git@deploy.eu2.frbit.com:.git - $ git add composer.json composer.lock - $ git add app/config/config_prod_secrets.php - -Commit and push - -.. code-block:: terminal - - $ git commit -m 'fortrabbit config' - $ git push fortrabbit master -u - -.. note:: - - Replace ```` with the name of your fortrabbit App. - -.. code-block:: terminal - - Commit received, starting build of branch master - - ––––––––––––––––––––––– ∙ƒ ––––––––––––––––––––––– - - B U I L D - - Checksum: - def1bb29911a62de26b1ddac6ef97fc76a5c647b - - Deployment file: - fortrabbit.yml - - Pre-script: - not found - 0ms - - Composer: - - - - - Loading composer repositories with package information - Installing dependencies (including require-dev) from lock file - Nothing to install or update - Generating autoload files - - - - - - 172ms - - Post-script: - not found - 0ms - - R E L E A S E - - Packaging: - 930ms - - Revision: - 1455788127289043421.def1bb29911a62de26b1ddac6ef97fc76a5c647b - - Size: - 9.7MB - - Uploading: - 500ms - - Build & release done in 1625ms, now queued for final distribution. - -.. note:: - - The first ``git push`` takes much longer as all composer dependencies get - downloaded. All subsequent deploys are done within seconds. - -That's it! Your application is being deployed on fortrabbit. More information -about `database migrations and tunneling`_ can be found in the fortrabbit -documentation. +For details on deploying to fortrabbit, see their official documentation: +`Install Symfony`_ -.. _`fortrabbit`: https://www.fortrabbit.com -.. _`Symfony install guide`: https://help.fortrabbit.com/install-symfony -.. _`fortrabbit.yml`: https://help.fortrabbit.com/deployment-file-v2 -.. _`database migrations and tunneling`: https://help.fortrabbit.com/install-symfony-2#toc-migrate-amp-other-database-commands -.. _`Sign up`: https://dashboard.fortrabbit.com +.. _`Install Symfony`: https://help.fortrabbit.com/install-symfony-3-uni diff --git a/deployment/heroku.rst b/deployment/heroku.rst index 1fe7748e35a..a7527805bd2 100644 --- a/deployment/heroku.rst +++ b/deployment/heroku.rst @@ -1,331 +1,12 @@ +:orphan: + .. index:: single: Deployment; Deploying to Heroku Cloud -Deploying to Heroku Cloud -========================= - -This step by step article describes how to deploy a Symfony web application to -the Heroku cloud platform. Its contents are based on `the original article`_ -published by Heroku. - -Setting up ----------- - -To set up a new Heroku website, first `sign up with Heroku`_ or sign in -with your credentials. Then download and install the `Heroku Toolbelt`_ on your -local computer. - -You can also check out the `getting Started with PHP on Heroku`_ guide to gain -more familiarity with the specifics of working with PHP applications on Heroku. - -Preparing your Application -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Deploying a Symfony application to Heroku doesn't require any change in its -code, but it requires some minor tweaks to its configuration. - -By default, the Symfony app will log into your application's ``var/log/`` -directory. This is not ideal as Heroku uses an `ephemeral file system`_. On -Heroku, the best way to handle logging is using `Logplex`_. And the best way to -send log data to Logplex is by writing to ``STDERR`` or ``STDOUT``. Luckily, -Symfony uses the excellent Monolog library for logging. So, a new log -destination is just a change to a config file away. - -Open the ``app/config/config_prod.yml`` file, locate the -``monolog/handlers/nested`` section (or create it if it doesn't exist yet) and -change the value of ``path`` from -``"%kernel.logs_dir%/%kernel.environment%.log"`` to ``"php://stderr"``: - -.. code-block:: yaml - - # app/config/config_prod.yml - monolog: - # ... - handlers: - # ... - nested: - # ... - path: 'php://stderr' - -Once the application is deployed, run ``heroku logs --tail`` to keep the -stream of logs from Heroku open in your terminal. - -Creating a new Application on Heroku ------------------------------------- - -To create a new Heroku application that you can push to, use the CLI ``create`` -command: - -.. code-block:: terminal - - $ heroku create - - Creating mighty-hamlet-1981 in organization heroku... done, stack is cedar - http://mighty-hamlet-1981.herokuapp.com/ | git@heroku.com:mighty-hamlet-1981.git - Git remote heroku added - -You are now ready to deploy the application as explained in the next section. - -Deploying your Application on Heroku ------------------------------------- - -Before your first deploy, you need to do just three more things, which are explained -below: - -#. :ref:`Create a Procfile ` - -#. :ref:`Set the Environment to prod ` - -#. :ref:`Push your Code to Heroku ` - -.. _heroku-procfile: -.. _creating-a-procfile: - -1) Create a Procfile -~~~~~~~~~~~~~~~~~~~~ - -By default, Heroku will launch an Apache web server together with PHP to serve -applications. However, a special circumstance apply to Symfony applications: -the document root is in the ``web/`` directory and not in the root directory -of the application. - -Create a new file called ``Procfile`` (without any extension) at the root -directory of the application and add just the following content: - -.. code-block:: text - - web: vendor/bin/heroku-php-apache2 web/ - -.. note:: - - If you prefer to use Nginx, which is also available on Heroku, you can create - a configuration file for it and point to it from your Procfile as described - in the `Heroku documentation`_: - - .. code-block:: text - - web: vendor/bin/heroku-php-nginx -C nginx_app.conf web/ - -If you prefer working on the command console, execute the following commands to -create the ``Procfile`` file and to add it to the repository: - -.. code-block:: terminal - - $ echo "web: vendor/bin/heroku-php-apache2 web/" > Procfile - $ git add . - $ git commit -m "Procfile for Apache and PHP" - [master 35075db] Procfile for Apache and PHP - 1 file changed, 1 insertion(+) - -.. _heroku-setting-env-to-prod: -.. _setting-the-prod-environment: - -2) Set the Environment to prod -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -During a deployment, Heroku runs ``composer install --no-dev`` to install all the -dependencies your application requires. However, typical `post-install-commands`_ -in ``composer.json``, e.g. to install assets or clear (or pre-warm) caches, run -using Symfony's ``dev`` environment by default. - -This is clearly not what you want - the app runs in "production" (even if you -use it just for an experiment, or as a staging environment), and so any build -steps should use the same ``prod`` environment as well. - -Thankfully, the solution to this problem is very simple: Symfony will pick up an -environment variable named ``SYMFONY_ENV`` and use that environment if nothing -else is explicitly set. As Heroku exposes all `config vars`_ as environment -variables, you can issue a single command to prepare your app for a deployment: - -.. code-block:: terminal - - $ heroku config:set SYMFONY_ENV=prod - -.. caution:: - - Be aware that dependencies from ``composer.json`` listed in the ``require-dev`` - section are never installed during a deploy on Heroku. This may cause problems - if your Symfony environment relies on such packages. The solution is to move these - packages from ``require-dev`` to the ``require`` section. - -.. _heroku-push-code: -.. _pushing-to-heroku: - -3) Push your Code to Heroku -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Next up, it's finally time to deploy your application to Heroku. If you are -doing this for the very first time, you may see a message such as the following: - -.. code-block:: text - - The authenticity of host 'heroku.com (50.19.85.132)' can't be established. - RSA key fingerprint is 8b:48:5e:67:0e:c9:16:47:32:f2:87:0c:1f:c8:60:ad. - Are you sure you want to continue connecting (yes/no)? - -In this case, you need to confirm by typing ``yes`` and hitting ```` key -- ideally after you've `verified that the RSA key fingerprint is correct`_. - -Then, deploy your application executing this command: - -.. code-block:: terminal - - $ git push heroku master - - Initializing repository, done. - Counting objects: 130, done. - Delta compression using up to 4 threads. - Compressing objects: 100% (107/107), done. - Writing objects: 100% (130/130), 70.88 KiB | 0 bytes/s, done. - Total 130 (delta 17), reused 0 (delta 0) - - -----> PHP app detected - - -----> Setting up runtime environment... - - PHP 5.5.12 - - Apache 2.4.9 - - Nginx 1.4.6 - - -----> Installing PHP extensions: - - opcache (automatic; bundled, using 'ext-opcache.ini') - - -----> Installing dependencies... - Composer version 64ac32fca9e64eb38e50abfadc6eb6f2d0470039 2014-05-24 20:57:50 - Loading composer repositories with package information - Installing dependencies from lock file - - ... - - Generating optimized autoload files - Creating the "app/config/parameters.yml" file - Clearing the cache for the dev environment with debug true - Installing assets using the hard copy option - Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework - Installing assets for Acme\DemoBundle into web/bundles/acmedemo - Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution - - -----> Building runtime environment... - - -----> Discovering process types - Procfile declares types -> web - - -----> Compressing... done, 61.5MB - - -----> Launching... done, v3 - http://mighty-hamlet-1981.herokuapp.com/ deployed to Heroku - - To git@heroku.com:mighty-hamlet-1981.git - * [new branch] master -> master - -And that's it! If you now open your browser, either by manually pointing -it to the URL ``heroku create`` gave you, or by using the Heroku Toolbelt, the -application will respond: - -.. code-block:: terminal - - $ heroku open - Opening mighty-hamlet-1981... done - -You should be seeing your Symfony application in your browser. - -.. caution:: - - If you take your first steps on Heroku using a fresh installation of - the Symfony Standard Edition, you may run into a 404 page not found error. - This is because the route for ``/`` is defined by the AcmeDemoBundle, but the - AcmeDemoBundle is only loaded in the dev environment (check out your - ``AppKernel`` class). Try opening ``/app/example`` from the AppBundle. - -Custom Compile Steps -~~~~~~~~~~~~~~~~~~~~ - -If you wish to execute additional custom commands during a build, you can leverage -Heroku's `custom compile steps`_. Imagine you want to remove the ``dev`` front controller -from your production environment on Heroku in order to avoid a potential vulnerability. -Adding a command to remove ``web/app_dev.php`` to Composer's `post-install-commands`_ would -work, but it also removes the controller in your local development environment on each -``composer install`` or ``composer update`` respectively. Instead, you can add a -`custom Composer command`_ named ``compile`` (this key name is a Heroku convention) to the -``scripts`` section of your ``composer.json``. The listed commands hook into Heroku's deploy -process: - -.. code-block:: json - - { - "scripts": { - "compile": [ - "rm web/app_dev.php" - ] - } - } - -This is also very useful to build assets on the production system, e.g. with Assetic: - -.. code-block:: json - - { - "scripts": { - "compile": [ - "bin/console assetic:dump" - ] - } - } - -.. sidebar:: Node.js Dependencies - - Building assets may depend on node packages, e.g. ``uglifyjs`` or ``uglifycss`` - for asset minification. Installing node packages during the deploy requires a node - installation. But currently, Heroku compiles your app using the PHP buildpack, which - is auto-detected by the presence of a ``composer.json`` file, and does not include a - node installation. Because the Node.js buildpack has a higher precedence than the PHP - buildpack (see `Heroku buildpacks`_), adding a ``package.json`` listing your node - dependencies makes Heroku opt for the Node.js buildpack instead: - - .. code-block:: json - - { - "name": "myApp", - "engines": { - "node": "0.12.x" - }, - "dependencies": { - "uglifycss": "*", - "uglify-js": "*" - } - } - - With the next deploy, Heroku compiles your app using the Node.js buildpack and - your npm packages become installed. On the other hand, your ``composer.json`` is - now ignored. To compile your app with both buildpacks, Node.js *and* PHP, you need - to use both buildpacks. To override buildpack auto-detection, you - need to explicitly set the buildpack: - - .. code-block:: terminal - - $ heroku buildpacks:set heroku/nodejs - Buildpack set. Next release on your-application will use heroku/nodejs. - Run git push heroku master to create a new release using this buildpack. - $ heroku buildpacks:set heroku/php --index 2 - Buildpack set. Next release on your-application will use: - 1. heroku/nodejs - 2. heroku/php - Run git push heroku master to create a new release using these buildpacks. +Deploying to Heroku +=================== - With the next deploy, you can benefit from both buildpacks. This setup also enables - your Heroku environment to make use of node based automatic build tools like - `Grunt`_ or `gulp`_. +To deploy to Heroku, see their official documentation: +`Getting Started with Symfony on Heroku`_. -.. _`the original article`: https://devcenter.heroku.com/articles/getting-started-with-symfony2 -.. _`sign up with Heroku`: https://signup.heroku.com/signup/dc -.. _`Heroku Toolbelt`: https://devcenter.heroku.com/articles/getting-started-with-php#set-up -.. _`getting Started with PHP on Heroku`: https://devcenter.heroku.com/articles/getting-started-with-php -.. _`ephemeral file system`: https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem -.. _`Logplex`: https://devcenter.heroku.com/articles/logplex -.. _`verified that the RSA key fingerprint is correct`: https://devcenter.heroku.com/articles/git-repository-ssh-fingerprints -.. _`post-install-commands`: https://getcomposer.org/doc/articles/scripts.md -.. _`config vars`: https://devcenter.heroku.com/articles/config-vars -.. _`custom compile steps`: https://devcenter.heroku.com/articles/php-support#custom-compile-step -.. _`custom Composer command`: https://getcomposer.org/doc/articles/scripts.md#writing-custom-commands -.. _`Heroku buildpacks`: https://devcenter.heroku.com/articles/buildpacks -.. _`Grunt`: http://gruntjs.com -.. _`gulp`: http://gulpjs.com -.. _`Heroku documentation`: https://devcenter.heroku.com/articles/custom-php-settings#nginx +.. _`Getting Started with Symfony on Heroku`: https://devcenter.heroku.com/articles/getting-started-with-symfony diff --git a/deployment/platformsh.rst b/deployment/platformsh.rst index 54b31fd2932..c124da18674 100644 --- a/deployment/platformsh.rst +++ b/deployment/platformsh.rst @@ -1,196 +1,12 @@ +:orphan: + .. index:: single: Deployment; Deploying to Platform.sh Deploying to Platform.sh ======================== -This step-by-step article describes how to deploy a Symfony web application to -`Platform.sh`_. You can read more about using Symfony with Platform.sh on the -official `Platform.sh documentation`_. - -Deploy an Existing Site ------------------------ - -In this guide, it is assumed your codebase is already versioned with Git. - -Get a Project on Platform.sh -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You need to `subscribe to a Platform.sh plan`_ and go through the checkout process. -Once your project is ready, give it a name and choose: **Import an existing site**. - -Prepare Your Application -~~~~~~~~~~~~~~~~~~~~~~~~ - -To deploy your Symfony application on Platform.sh, you simply need to add a -``.platform.app.yaml`` at the root of your Git repository which will tell -Platform.sh how to deploy your application (read more about -`Platform.sh configuration files`_). - -.. code-block:: yaml - - # .platform.app.yaml - - # This file describes an application. You can have multiple applications - # in the same project. - - # The name of this app. Must be unique within a project. - name: myphpproject - - # The type of the application to build. - type: php:5.6 - build: - flavor: composer - - # The relationships of the application with services or other applications. - # The left-hand side is the name of the relationship as it will be exposed - # to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand - # side is in the form `:`. - relationships: - database: 'mysql:mysql' - - # The configuration of app when it is exposed to the web. - web: - # The public directory of the app, relative to its root. - document_root: '/web' - # The front-controller script to send non-static requests to. - passthru: '/app.php' - - # The size of the persistent disk of the application (in MB). - disk: 2048 - - # The mounts that will be performed when the package is deployed. - mounts: - '/var/cache': 'shared:files/cache' - '/var/logs': 'shared:files/logs' - '/var/sessions': 'shared:files/sessions' - - # The hooks that will be performed when the package is deployed. - hooks: - build: | - rm web/app_dev.php - bin/console --env=prod assetic:dump --no-debug - deploy: | - bin/console --env=prod cache:clear - -For best practices, you should also add a ``.platform`` folder at the root of -your Git repository which contains the following files: - -.. code-block:: yaml - - # .platform/routes.yaml - "http://{default}/": - type: upstream - # the first part should be your project name - upstream: 'myphpproject:php' - -.. code-block:: yaml - - # .platform/services.yaml - mysql: - type: mysql - disk: 2048 - -An example of these configurations can be found on `GitHub`_. The list of -`available services`_ can be found on the Platform.sh documentation. - -Configure Database Access -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Platform.sh overrides your database specific configuration via importing the -following file (it's your role to add this file to your code base):: - - // app/config/parameters_platform.php - setParameter('database_driver', 'pdo_' . $endpoint['scheme']); - $container->setParameter('database_host', $endpoint['host']); - $container->setParameter('database_port', $endpoint['port']); - $container->setParameter('database_name', $endpoint['path']); - $container->setParameter('database_user', $endpoint['username']); - $container->setParameter('database_password', $endpoint['password']); - $container->setParameter('database_path', ''); - } - - # Store session into /tmp. - ini_set('session.save_path', '/tmp/sessions'); - -Make sure this file is listed in your *imports* (after the default ``parameters.yml`` -file): - -.. code-block:: yaml - - # app/config/config.yml - imports: - - { resource: parameters.yml } - - { resource: parameters_platform.php } - -Deploy your Application -~~~~~~~~~~~~~~~~~~~~~~~ - -Now you need to add a remote to Platform.sh in your Git repository (copy the -command that you see on the Platform.sh web UI): - -.. code-block:: terminal - - $ git remote add platform [PROJECT-ID]@git.[CLUSTER].platform.sh:[PROJECT-ID].git - -``PROJECT-ID`` - Unique identifier of your project. Something like ``kjh43kbobssae`` -``CLUSTER`` - Server location where your project is deployed. It can be ``eu`` or ``us`` - -Commit the Platform.sh specific files created in the previous section: - -.. code-block:: terminal - - $ git add .platform.app.yaml .platform/* - $ git add app/config/config.yml app/config/parameters_platform.php - $ git commit -m "Adding Platform.sh configuration files." - -Push your code base to the newly added remote: - -.. code-block:: terminal - - $ git push platform master - -That's it! Your application is being deployed on Platform.sh and you'll soon be -able to access it in your browser. - -Every code change that you do from now on will be pushed to Git in order to -redeploy your environment on Platform.sh. - -More information about `migrating your database and files`_ can be found -on the Platform.sh documentation. - -Deploy a new Site ------------------ - -You can start a new `Platform.sh project`_. Choose the development plan and go -through the checkout process. - -Once your project is ready, give it a name and choose: **Create a new site**. -Choose the *Symfony* stack and a starting point such as *Standard*. - -That's it! Your Symfony application will be bootstrapped and deployed. You'll -soon be able to see it in your browser. +To deploy to Platform.sh, see their official documentation: +`Symfony Platform.sh Documentation`_. -.. _`Platform.sh`: https://platform.sh -.. _`Platform.sh documentation`: https://docs.platform.sh/frameworks/symfony.html -.. _`Platform.sh project`: https://accounts.platform.sh/platform/buy-now -.. _`subscribe to a Platform.sh plan`: https://accounts.platform.sh/platform/buy-now -.. _`Platform.sh configuration files`: https://docs.platform.sh/configuration/services.html -.. _`GitHub`: https://github.com/platformsh/platformsh-examples -.. _`available services`: https://docs.platform.sh/reference/configuration-files/#configure-services -.. _`migrating your database and files`: https://docs.platform.sh/tutorials/migrating.html +.. _`Symfony Platform.sh Documentation`: https://docs.platform.sh/frameworks/symfony.html diff --git a/deployment/proxies.rst b/deployment/proxies.rst index 25a8026108c..436b6f23f10 100644 --- a/deployment/proxies.rst +++ b/deployment/proxies.rst @@ -26,7 +26,7 @@ and what headers your reverse proxy uses to send information: .. code-block:: php - // web/app.php + // public/index.php // ... $request = Request::createFromGlobals(); @@ -65,7 +65,7 @@ In this case, you'll need to - *very carefully* - trust *all* proxies. .. code-block:: php - // web/app.php + // public/index.php // ... Request::setTrustedProxies( diff --git a/doctrine.rst b/doctrine.rst index cb43023746e..ecd2b1e9751 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -4,666 +4,462 @@ Databases and the Doctrine ORM ============================== -One of the most common and challenging tasks for any application -involves persisting and reading information to and from a database. Although -the Symfony Framework doesn't integrate any component to work with databases, -it provides tight integration with a third-party library called `Doctrine`_. -Doctrine's sole goal is to give you powerful tools to make database interactions -easy and flexible. - -In this chapter, you'll learn how to start leveraging Doctrine in your Symfony projects -to give you rich database interactions. +Symfony doesn't provide a component to work with the database, but it *does* provide +tight integration with a third-party library called `Doctrine`_. .. note:: - Doctrine is totally decoupled from Symfony and using it is optional. - This chapter is all about the Doctrine ORM, which aims to let you map - objects to a relational database (such as *MySQL*, *PostgreSQL* or - *Microsoft SQL*). If you prefer to use raw database queries, this is - easy, and explained in the ":doc:`/doctrine/dbal`" article. + This article is all about using the Doctrine ORM. If you prefer to use raw + database queries, see the ":doc:`/doctrine/dbal`" article instead. + + You can also persist data to `MongoDB`_ using Doctrine ODM library. See the + "`DoctrineMongoDBBundle`_" documentation. + +Installing Doctrine +------------------- - You can also persist data to `MongoDB`_ using Doctrine ODM library. For - more information, read the "`DoctrineMongoDBBundle`_" - documentation. +First, install Doctrine, as well as the MakerBundle, which will help generate some +code: -A Simple Example: A Product ---------------------------- +.. code-block:: terminal -The easiest way to understand how Doctrine works is to see it in action. -In this section, you'll configure your database, create a ``Product`` object, -persist it to the database and fetch it back out. + composer require doctrine + composer require maker --dev Configuring the Database ~~~~~~~~~~~~~~~~~~~~~~~~ -Before you really begin, you'll need to configure your database connection -information. By convention, this information is usually configured in an -``app/config/parameters.yml`` file: - -.. code-block:: yaml +The database connection information is stored as an environment variable called +``DATABASE_URL``. For development, you can find and customize this inside ``.env``: - # app/config/parameters.yml - parameters: - database_host: localhost - database_name: test_project - database_user: root - database_password: password +.. code-block:: text - # ... + # .env -.. note:: + # customize this line! + DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" - Defining the configuration via ``parameters.yml`` is just a convention. - The parameters defined in that file are referenced by the main configuration - file when setting up Doctrine: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - driver: pdo_mysql - host: '%database_host%' - dbname: '%database_name%' - user: '%database_user%' - password: '%database_password%' - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('doctrine', array( - 'dbal' => array( - 'driver' => 'pdo_mysql', - 'host' => '%database_host%', - 'dbname' => '%database_name%', - 'user' => '%database_user%', - 'password' => '%database_password%', - ), - )); - - By separating the database information into a separate file, you can - easily keep different versions of the file on each server. You can also - easily store database configuration (or any sensitive information) outside - of your project, like inside your Apache configuration, for example. For - more information, see :doc:`/configuration/external_parameters`. - -Now that Doctrine can connect to your database, the following command -can automatically generate an empty ``test_project`` database for you: + # to use sqlite: + # DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db" -.. code-block:: terminal +.. caution:: - $ php bin/console doctrine:database:create + If the username, password or database name contain any character considered + special in a URI (such as ``!``, ``@``, ``$``, ``#``), you must encode them. + See `RFC 3986`_ for the full list of reserved characters or use the + :phpfunction:`urlencode` function to encode them. -.. sidebar:: Setting up the Database to be UTF8 +Now that your connection parameters are setup, Doctrine can create the ``db_name`` +database for you: - One mistake even seasoned developers make when starting a Symfony project - is forgetting to set up default charset and collation on their database, - ending up with latin type collations, which are default for most databases. - They might even remember to do it the very first time, but forget that - it's all gone after running a relatively common command during development: +.. code-block:: terminal - .. code-block:: terminal + $ php bin/console doctrine:database:create - $ php bin/console doctrine:database:drop --force - $ php bin/console doctrine:database:create - - Setting UTF8 defaults for MySQL is as simple as adding a few lines to - your configuration file (typically ``my.cnf``): - - .. code-block:: ini - - [mysqld] - # Version 5.5.3 introduced "utf8mb4", which is recommended - collation-server = utf8mb4_unicode_ci # Replaces utf8_unicode_ci - character-set-server = utf8mb4 # Replaces utf8 - - You can also change the defaults for Doctrine so that the generated SQL - uses the correct character set. - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - charset: utf8mb4 - default_table_options: - charset: utf8mb4 - collate: utf8mb4_unicode_ci - - .. code-block:: xml - - - - - - - - utf8mb4 - utf8mb4_unicode_ci - - - - - .. code-block:: php - - // app/config/config.php - $configuration->loadFromExtension('doctrine', array( - 'dbal' => array( - 'charset' => 'utf8mb4', - 'default_table_options' => array( - 'charset' => 'utf8mb4', - 'collate' => 'utf8mb4_unicode_ci', - ) - ), - )); - - We recommend against MySQL's ``utf8`` character set, since it does not - support 4-byte unicode characters, and strings containing them will be - truncated. This is fixed by the `newer utf8mb4 character set`_. +There are more options in ``config/packages/doctrine.yaml`` that you can configure, +including your ``server_version`` (e.g. 5.7 if you're using MySQL 5.7), which may +affect how Doctrine functions. -.. note:: +.. tip:: - If you want to use SQLite as your database, you need to set the path - where your database file should be stored: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - driver: pdo_sqlite - path: '%kernel.project_dir%/app/sqlite.db' - charset: UTF8 - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('doctrine', array( - 'dbal' => array( - 'driver' => 'pdo_sqlite', - 'path' => '%kernel.project_dir%/app/sqlite.db', - 'charset' => 'UTF-8', - ), - )); + There are many other Doctrine commands. Run ``php bin/console list doctrine`` + to see a full list. Creating an Entity Class -~~~~~~~~~~~~~~~~~~~~~~~~ +------------------------ Suppose you're building an application where products need to be displayed. Without even thinking about Doctrine or databases, you already know that -you need a ``Product`` object to represent those products. Create this class -inside the ``Entity`` directory of your AppBundle:: +you need a ``Product`` object to represent those products. - // src/AppBundle/Entity/Product.php - namespace AppBundle\Entity; +.. _doctrine-adding-mapping: - class Product - { - private $name; - private $price; - private $description; - } +You can use the ``make:entity`` command to create this class and any fields you +need. The command will ask you some questions - answer them like done below: -The class - often called an "entity", meaning *a basic class that holds data* - -is simple and helps fulfill the business requirement of needing products -in your application. This class can't be persisted to a database yet - it's -just a simple PHP class. +.. code-block:: terminal -.. tip:: + $ php bin/console make:entity - Once you learn the concepts behind Doctrine, you can have Doctrine create - simple entity classes for you. This will ask you interactive questions - to help you build any entity: + Class name of the entity to create or update: + > Product - .. code-block:: terminal + New property name (press to stop adding fields): + > name - $ php bin/console doctrine:generate:entity + Field type (enter ? to see all types) [string]: + > string -.. index:: - single: Doctrine; Adding mapping metadata + Field length [255]: + > 255 -.. _doctrine-adding-mapping: + Can this field be null in the database (nullable) (yes/no) [no]: + > no -Add Mapping Information -~~~~~~~~~~~~~~~~~~~~~~~ + New property name (press to stop adding fields): + > price -Doctrine allows you to work with databases in a much more interesting way -than just fetching rows of scalar data into an array. Instead, Doctrine -allows you to fetch entire *objects* out of the database, and to persist -entire objects to the database. For Doctrine to be able to do this, you -must *map* your database tables to specific PHP classes, and the columns -on those tables must be mapped to specific properties on their corresponding -PHP classes. + Field type (enter ? to see all types) [string]: + > integer -.. image:: /_images/doctrine/mapping_single_entity.png - :align: center + Can this field be null in the database (nullable) (yes/no) [no]: + > no -You'll provide this mapping information in the form of "metadata", a collection -of rules that tells Doctrine exactly how the ``Product`` class and its -properties should be *mapped* to a specific database table. This metadata -can be specified in a number of different formats, including YAML, XML or -directly inside the ``Product`` class via DocBlock annotations: + New property name (press to stop adding fields): + > + (press enter again to finish) -.. configuration-block:: +.. versionadded:: + The interactive behavior of the ``make:entity`` command was introduced + in MakerBundle 1.3. - .. code-block:: php-annotations +Woh! You now have a new ``src/Entity/Product.php`` file:: - // src/AppBundle/Entity/Product.php - namespace AppBundle\Entity; + // src/Entity/Product.php + namespace App\Entity; - use Doctrine\ORM\Mapping as ORM; + use Doctrine\ORM\Mapping as ORM; + + /** + * @ORM\Entity(repositoryClass="App\Repository\ProductRepository") + */ + class Product + { + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; /** - * @ORM\Entity - * @ORM\Table(name="product") + * @ORM\Column(type="string", length=255) */ - class Product + private $name; + + /** + * @ORM\Column(type="integer") + */ + private $price; + + public function getId() { - /** - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @ORM\Column(type="string", length=100) - */ - private $name; - - /** - * @ORM\Column(type="decimal", scale=2) - */ - private $price; - - /** - * @ORM\Column(type="text") - */ - private $description; + return $this->id; } - .. code-block:: yaml - - # src/AppBundle/Resources/config/doctrine/Product.orm.yml - AppBundle\Entity\Product: - type: entity - table: product - id: - id: - type: integer - generator: { strategy: AUTO } - fields: - name: - type: string - length: 100 - price: - type: decimal - scale: 2 - description: - type: text - - .. code-block:: xml - - - - - - - - - - - - - - + // ... getter and setter methods + } .. note:: - A bundle can accept only one metadata definition format. For example, it's - not possible to mix YAML metadata definitions with annotated PHP entity - class definitions. + Confused why the price is an integer? Don't worry: this is just an example. + But, storing prices as integers (e.g. 100 = $1 USD) can avoid rounding issues. -.. tip:: +This class is called an "entity". And soon, you'll be able to save and query Product +objects to a ``product`` table in your database. Each property in the ``Product`` +entity can be mapped to a column in that table. This is usually done with annotations: +the ``@ORM\...`` comments that you see above each property: - The table name is optional and if omitted, will be determined automatically - based on the name of the entity class. - -Doctrine allows you to choose from a wide variety of different field types, -each with their own options. For information on the available field types, -see the :ref:`doctrine-field-types` section. +.. image:: /_images/doctrine/mapping_single_entity.png + :align: center -.. seealso:: +The ``make:entity`` command is a tool to make life easier. But this is *your* code: +add/remove fields, add/remove methods or update configuration. - You can also check out Doctrine's `Basic Mapping Documentation`_ for - all details about mapping information. If you use annotations, you'll - need to prepend all annotations with ``ORM\`` (e.g. ``ORM\Column(...)``), - which is not shown in Doctrine's documentation. You'll also need to include - the ``use Doctrine\ORM\Mapping as ORM;`` statement, which *imports* the - ``ORM`` annotations prefix. +Doctrine supports a wide variety of field types, each with their own options. +To see a full list, check out `Doctrine's Mapping Types documentation`_. +If you want to use XML instead of annotations, add ``type: xml`` and +``dir: '%kernel.project_dir%/config/doctrine'`` to the entity mappings in your +``config/packages/doctrine.yaml`` file. .. caution:: - Be careful if the names of your entity classes (or their properties) - are also reserved SQL keywords like ``GROUP`` or ``USER``. For example, - if your entity's class name is ``Group``, then, by default, the corresponding - table name would be ``group``. This will cause an SQL error in some database - engines. See Doctrine's `Reserved SQL keywords documentation`_ for details - on how to properly escape these names. Alternatively, if you're free - to choose your database schema, simply map to a different table name - or column name. See Doctrine's `Creating Classes for the Database`_ - and `Property Mapping`_ documentation. + Be careful not to use reserved SQL keywords as your table or column names + (e.g. ``GROUP`` or ``USER``). See Doctrine's `Reserved SQL keywords documentation`_ + for details on how to escape these. Or, change the table name with + ``@ORM\Table(name="groups")`` above the class or configure the column name with + the ``name="group_name"`` option. -.. note:: +.. _doctrine-creating-the-database-tables-schema: - When using another library or program (e.g. Doxygen) that uses annotations, - you should place the ``@IgnoreAnnotation`` annotation on the class to - indicate which annotations Symfony should ignore. +Migrations: Creating the Database Tables/Schema +----------------------------------------------- - For example, to prevent the ``@fn`` annotation from throwing an exception, - add the following:: +The ``Product`` class is fully-configured and ready to save to a ``product`` table. +Of course, your database doesn't actually have the ``product`` table yet. To add +it, you can leverage the `DoctrineMigrationsBundle`_, which is already installed: - /** - * @IgnoreAnnotation("fn") - */ - class Product - // ... +.. code-block:: terminal -.. tip:: + $ php bin/console make:migration - After creating your entities you should validate the mappings with the - following command: +If everything worked, you should see something like this: - .. code-block:: terminal + SUCCESS! - $ php bin/console doctrine:schema:validate + Next: Review the new migration "src/Migrations/Version20180207231217.php" + Then: Run the migration with php bin/console doctrine:migrations:migrate -.. _doctrine-generating-getters-and-setters: +If you open this file, it contains the SQL needed to update your database! To run +that SQL, execute your migrations: -Generating Getters and Setters -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. code-block:: terminal -Even though Doctrine now knows how to persist a ``Product`` object to the -database, the class itself isn't really useful yet. Since ``Product`` is just -a regular PHP class with ``private`` properties, you need to create ``public`` -getter and setter methods (e.g. ``getName()``, ``setName($name)``) in order -to access its properties in the rest of your application's code. Add these -methods manually or with your own IDE. + $ php bin/console doctrine:migrations:migrate -.. _doctrine-creating-the-database-tables-schema: +This command executes all migration files that have not already been run against +your database. You should run this command on production when you deploy to keep +your production database up-to-date. -Creating the Database Tables/Schema -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Migrations & Adding more Fields +------------------------------- -You now have a usable ``Product`` class with mapping information so that -Doctrine knows exactly how to persist it. Of course, you don't yet have the -corresponding ``product`` table in your database. Fortunately, Doctrine can -automatically create all the database tables needed for every known entity -in your application. To do this, run: +But what if you need to add a new field property to ``Product``, like a ``description``? +It's easy to add the new property by hand. But, you can also use ``make:entity`` +again: .. code-block:: terminal - $ php bin/console doctrine:schema:update --force + $ php bin/console make:entity + + Class name of the entity to create or update + > Product + + New property name (press to stop adding fields): + > description + + Field type (enter ? to see all types) [string]: + > text + + Can this field be null in the database (nullable) (yes/no) [no]: + > no + + New property name (press to stop adding fields): + > + (press enter again to finish) + +This adds the new ``description`` property and ``getDescription()`` and ``setDescription()`` +methods: + +.. code-block:: diff + + // src/Entity/Product.php + // ... + + class Product + { + // ... + + + /** + + * @ORM\Column(type="text") + + */ + + private $description; + + // getDescription() & setDescription() were also added + } + +The new property is mapped, but it doesn't exist yet in the ``product`` table. No +problem! Just generate a new migration: + +.. code-block:: terminal + + $ php bin/console make:migration + +This time, the SQL in the generated file will look like this: + +.. code-block:: sql + + ALTER TABLE product ADD description LONGTEXT NOT NULL + +The migration system is *smart*. It compares all of your entities with the current +state of the database and generates the SQL needed to synchronize them! Just like +before, execute your migrations: + +.. code-block:: terminal + + $ php bin/console doctrine:migrations:migrate + +This will only execute the *one* new migration file, because DoctrineMigrationsBundle +knows that the first migration was already executed earlier. Behind the scenes, it +manages a ``migration_versions`` table to track this. + +Each time you make a change to your schema, run these two commands to generate the +migration and then execute it. Be sure to commit the migration files and execute +them when you deploy. + +.. _doctrine-generating-getters-and-setters: .. tip:: - Actually, this command is incredibly powerful. It compares what - your database *should* look like (based on the mapping information of - your entities) with how it *actually* looks, and executes the SQL statements - needed to *update* the database schema to where it should be. In other - words, if you add a new property with mapping metadata to ``Product`` - and run this command, it will execute the "ALTER TABLE" statement needed - to add that new column to the existing ``product`` table. + If you prefer to add new properties manually, the ``make:entity`` command can + generate the getter & setter methods for you: - An even better way to take advantage of this functionality is via - `migrations`_, which allow you to generate these SQL statements and store - them in migration classes that can be run systematically on your production - server in order to update and track changes to your database schema safely - and reliably. + .. code-block:: terminal - Whether or not you take advantage of migrations, the ``doctrine:schema:update`` - command should only be used during development. It should not be used in - a production environment. + $ php bin/console make:entity --regenerate -Your database now has a fully-functional ``product`` table with columns that -match the metadata you've specified. + If you make some changes and want to regenerate *all* getter/setter methods, + also pass ``--overwrite``. Persisting Objects to the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------------- + +It's time to save a ``Product`` object to the database! Let's create a new controller +to experiment: + +.. code-block:: terminal + + $ php bin/console make:controller ProductController + +Inside the controller, you can create a new ``Product`` object, set data on it, +and save it! -Now that you have mapped the ``Product`` entity to its corresponding ``product`` -table, you're ready to persist ``Product`` objects to the database. From inside -a controller, this is pretty easy. Add the following method to the -``DefaultController`` of the bundle:: +.. code-block:: php - // src/AppBundle/Controller/DefaultController.php + // src/Controller/ProductController.php + namespace App\Controller; // ... - use AppBundle\Entity\Product; - use Symfony\Component\HttpFoundation\Response; - use Doctrine\ORM\EntityManagerInterface; + use App\Entity\Product; - public function createAction() + class ProductController extends Controller { - // you can fetch the EntityManager via $this->getDoctrine() - // or you can add an argument to your action: createAction(EntityManagerInterface $entityManager) - $entityManager = $this->getDoctrine()->getManager(); + /** + * @Route("/product", name="product") + */ + public function index() + { + // you can fetch the EntityManager via $this->getDoctrine() + // or you can add an argument to your action: index(EntityManagerInterface $entityManager) + $entityManager = $this->getDoctrine()->getManager(); - $product = new Product(); - $product->setName('Keyboard'); - $product->setPrice(19.99); - $product->setDescription('Ergonomic and stylish!'); + $product = new Product(); + $product->setName('Keyboard'); + $product->setPrice(1999); + $product->setDescription('Ergonomic and stylish!'); - // tells Doctrine you want to (eventually) save the Product (no queries yet) - $entityManager->persist($product); + // tell Doctrine you want to (eventually) save the Product (no queries yet) + $entityManager->persist($product); - // actually executes the queries (i.e. the INSERT query) - $entityManager->flush(); + // actually executes the queries (i.e. the INSERT query) + $entityManager->flush(); - return new Response('Saved new product with id '.$product->getId()); + return new Response('Saved new product with id '.$product->getId()); + } } - // if you have multiple entity managers, use the registry to fetch them - public function editAction() - { - $doctrine = $this->getDoctrine(); - $entityManager = $doctrine->getManager(); - $otherEntityManager = $doctrine->getManager('other_connection'); - } +Try it out! -.. note:: + http://localhost:8000/product + +Congratulations! You just created your first row in the ``product`` table. To prove it, +you can query the database directly: - If you're following along with this example, you'll need to create a - route that points to this action to see it work. +.. code-block:: terminal + + $ php bin/console doctrine:query:sql 'SELECT * FROM product' + + # on Windows systems not using Powershell, run this command instead: + # php bin/console doctrine:query:sql "SELECT * FROM product" Take a look at the previous example in more detail: .. _doctrine-entity-manager: -* **line 12** The ``$this->getDoctrine()->getManager()`` method gets Doctrine's +* **line 16** The ``$this->getDoctrine()->getManager()`` method gets Doctrine's *entity manager* object, which is the most important object in Doctrine. It's responsible for saving objects to, and fetching objects from, the database. -* **lines 14-17** In this section, you instantiate and work with the ``$product`` +* **lines 18-21** In this section, you instantiate and work with the ``$product`` object like any other normal PHP object. -* **line 20** The ``persist($product)`` call tells Doctrine to "manage" the +* **line 24** The ``persist($product)`` call tells Doctrine to "manage" the ``$product`` object. This does **not** cause a query to be made to the database. -* **line 23** When the ``flush()`` method is called, Doctrine looks through +* **line 27** When the ``flush()`` method is called, Doctrine looks through all of the objects that it's managing to see if they need to be persisted to the database. In this example, the ``$product`` object's data doesn't exist in the database, so the entity manager executes an ``INSERT`` query, creating a new row in the ``product`` table. -.. note:: - - In fact, since Doctrine is aware of all your managed entities, when you call - the ``flush()`` method, it calculates an overall changeset and executes - the queries in the correct order. It utilizes cached prepared statement to - slightly improve the performance. For example, if you persist a total of 100 - ``Product`` objects and then subsequently call ``flush()``, Doctrine will - execute 100 ``INSERT`` queries using a single prepared statement object. - .. note:: If the ``flush()`` call fails, a ``Doctrine\ORM\ORMException`` exception is thrown. See `Transactions and Concurrency`_. -Whether creating or updating objects, the workflow is always the same. In -the next section, you'll see how Doctrine is smart enough to automatically -issue an ``UPDATE`` query if the entity already exists in the database. - -.. tip:: - - Doctrine provides a library that allows you to programmatically load testing - data into your project (i.e. "fixture data"). For information, see - the "`DoctrineFixturesBundle`_" documentation. +Whether you're creating or updating objects, the workflow is always the same: Doctrine +is smart enough to know if it should INSERT of UPDATE your entity. Fetching Objects from the Database -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------------- -Fetching an object back out of the database is even easier. For example, -suppose you've configured a route to display a specific ``Product`` based -on its ``id`` value:: +Fetching an object back out of the database is even easier. Suppose you want to +be able to go to ``/product/1`` to see your new product:: - public function showAction($productId) + // src/Controller/ProductController.php + // ... + + /** + * @Route("/product/{id}", name="product_show") + */ + public function showAction($id) { $product = $this->getDoctrine() ->getRepository(Product::class) - ->find($productId); + ->find($id); if (!$product) { throw $this->createNotFoundException( - 'No product found for id '.$productId + 'No product found for id '.$id ); } - // ... do something, like pass the $product object into a template + return new Response('Check out this great product: '.$product->getName()); + + // or render a template + // in the template, print things with {{ product.name }} + // return $this->render('product/show.html.twig', ['product' => $product]); } -.. tip:: +Try it out! - You can achieve the equivalent of this without writing any code by using - the ``@ParamConverter`` shortcut. See the `FrameworkExtraBundle documentation`_ - for more details. + http://localhost:8000/product/1 When you query for a particular type of object, you always use what's known as its "repository". You can think of a repository as a PHP class whose only -job is to help you fetch entities of a certain class. You can access the -repository object for an entity class via:: - - $repository = $this->getDoctrine() - ->getRepository(Product::class); - -.. note:: - - You can also use ``AppBundle:Product`` syntax. This string is a shortcut you can use anywhere - in Doctrine instead of the full class name of the entity (i.e. ``AppBundle\Entity\Product``). - As long as your entity lives under the ``Entity`` namespace of your bundle, - this will work. +job is to help you fetch entities of a certain class. -Once you have a repository object, you can access all sorts of helpful methods:: +Once you have a repository object, you have many helper methods:: $repository = $this->getDoctrine()->getRepository(Product::class); - // looks for a single product by its primary key (usually "id") - $product = $repository->find($productId); + // look for a single Product by its primary key (usually "id") + $product = $repository->find($id); - // dynamic method names to find a single product based on a column value - $product = $repository->findOneById($productId); - $product = $repository->findOneByName('Keyboard'); + // look for a single Product by name + $product = $repository->findOneBy(['name' => 'Keyboard']); + // or find by name and price + $product = $repository->findOneBy([ + 'name' => 'Keyboard', + 'price' => 1999, + ]); - // dynamic method names to find a group of products based on a column value - $products = $repository->findByPrice(19.99); + // look for multiple Product objects matching the name, ordered by price + $products = $repository->findBy( + ['name' => 'Keyboard'], + ['price' => 'ASC'] + ); - // finds *all* products + // look for *all* Product objects $products = $repository->findAll(); -.. note:: - - Of course, you can also issue complex queries, which you'll learn more - about in the :ref:`doctrine-queries` section. - -You can also take advantage of the useful ``findBy()`` and ``findOneBy()`` methods -to easily fetch objects based on multiple conditions:: - - $repository = $this->getDoctrine()->getRepository(Product::class); - - // looks for a single product matching the given name and price - $product = $repository->findOneBy( - array('name' => 'Keyboard', 'price' => 19.99) - ); - - // looks for multiple products matching the given name, ordered by price - $products = $repository->findBy( - array('name' => 'Keyboard'), - array('price' => 'ASC') - ); +You can also add *custom* methods for more complex queries! More on that later in +the :ref:`doctrine-queries` section. .. tip:: - When rendering a page requires to make some database calls, the web debug - toolbar at the bottom of the page displays the number of queries and the - time it took to execute them: + When rendering an HTML page, the web debug toolbar at the bottom of the page + will display the number of queries and the time it took to execute them: .. image:: /_images/doctrine/doctrine_web_debug_toolbar.png :align: center @@ -671,32 +467,66 @@ to easily fetch objects based on multiple conditions:: If the number of database queries is too high, the icon will turn yellow to indicate that something may not be correct. Click on the icon to open the - Symfony Profiler and see the exact queries that were executed. + Symfony Profiler and see the exact queries that were executed. If you don't + see the web debug toolbar, try running ``composer require profiler`` to install + it. -Updating an Object -~~~~~~~~~~~~~~~~~~ +Automatically Fetching Objects (ParamConverter) +----------------------------------------------- + +In many cases, you can use the `SensioFrameworkExtraBundle`_ to do the query +for you automatically! First, install the bundle in case you don't have it: + +.. code-block:: terminal -Once you've fetched an object from Doctrine, updating it is easy. Suppose -you have a route that maps a product id to an update action in a controller:: + $ composer require annotations - use AppBundle\Entity\Product; +Now, simplify your controller:: + + // src/Controller/ProductController.php + + use App\Entity\Product; // ... - public function updateAction($productId) + /** + * @Route("/product/{id}", name="product_show") + */ + public function showAction(Product $product) + { + // use the Product! + // ... + } + +That's it! The bundle uses the ``{id}`` from the route to query for the ``Product`` +by the ``id`` column. If it's not found, a 404 page is generated. + +There are many more options you can use. Read more about the `ParamConverter`_. + +Updating an Object +------------------ + +Once you've fetched an object from Doctrine, updating it is easy:: + + /** + * @Route("/product/edit/{id}") + */ + public function updateAction($id) { $entityManager = $this->getDoctrine()->getManager(); - $product = $entityManager->getRepository(Product::class)->find($productId); + $product = $entityManager->getRepository(Product::class)->find($id); if (!$product) { throw $this->createNotFoundException( - 'No product found for id '.$productId + 'No product found for id '.$id ); } $product->setName('New product name!'); $entityManager->flush(); - return $this->redirectToRoute('homepage'); + return $this->redirectToRoute('product_show', [ + 'id' => $product->getId() + ]); } Updating an object involves just three steps: @@ -705,13 +535,11 @@ Updating an object involves just three steps: #. modifying the object; #. calling ``flush()`` on the entity manager. -Notice that calling ``$entityManager->persist($product)`` isn't necessary. Recall that -this method simply tells Doctrine to manage or "watch" the ``$product`` object. -In this case, since you fetched the ``$product`` object from Doctrine, it's -already managed. +You *can* call ``$entityManager->persist($product)``, but it isn't necessary: +Doctrine is already "watching" your object for changes. Deleting an Object -~~~~~~~~~~~~~~~~~~ +------------------ Deleting an object is very similar, but requires a call to the ``remove()`` method of the entity manager:: @@ -720,168 +548,196 @@ method of the entity manager:: $entityManager->flush(); As you might expect, the ``remove()`` method notifies Doctrine that you'd -like to remove the given object from the database. The actual ``DELETE`` query, -however, isn't actually executed until the ``flush()`` method is called. +like to remove the given object from the database. The ``DELETE`` query isn't +actually executed until the ``flush()`` method is called. .. _doctrine-queries: -Querying for Objects --------------------- +Querying for Objects: The Repository +------------------------------------ You've already seen how the repository object allows you to run basic queries without any work:: + // from inside a controller $repository = $this->getDoctrine()->getRepository(Product::class); - $product = $repository->find($productId); - $product = $repository->findOneByName('Keyboard'); + $product = $repository->find($id); -Of course, Doctrine also allows you to write more complex queries using the -Doctrine Query Language (DQL). DQL is similar to SQL except that you should -imagine that you're querying for one or more objects of an entity class (e.g. ``Product``) -instead of querying for rows on a table (e.g. ``product``). +But what if you need a more complex query? When you generated your entity with +``make:entity``, the command *also* generated a ``ProductRepository`` class:: -When querying in Doctrine, you have two main options: writing pure DQL queries -or using Doctrine's Query Builder. + // src/Repository/ProductRepository.php + namespace App\Repository; -Querying for Objects with DQL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + use App\Entity\Product; + use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; + use Symfony\Bridge\Doctrine\RegistryInterface; -Imagine that you want to query for products that cost more than ``19.99``, -ordered from least to most expensive. You can use DQL, Doctrine's native -SQL-like language, to construct a query for this scenario:: + class ProductRepository extends ServiceEntityRepository + { + public function __construct(RegistryInterface $registry) + { + parent::__construct($registry, Product::class); + } + } - $query = $entityManager->createQuery( - 'SELECT p - FROM AppBundle:Product p - WHERE p.price > :price - ORDER BY p.price ASC' - )->setParameter('price', 19.99); +When you fetch your repository (i.e. ``->getRepository(Product::class)``), it is +*actually* an instance of *this* object! This is because of the ``repositoryClass`` +config that was generated at the top of your ``Product`` entity class. - $products = $query->getResult(); +Suppose you want to query for all Product objects greater than a certain price. Add +a new method for this to your repository:: -If you're comfortable with SQL, then DQL should feel very natural. The biggest -difference is that you need to think in terms of selecting PHP objects, -instead of rows in a database. For this reason, you select *from* the -``AppBundle:Product`` *entity* (an optional shortcut for the -``AppBundle\Entity\Product`` class) and then alias it as ``p``. + // src/Repository/ProductRepository.php -.. tip:: + // ... + class ProductRepository extends ServiceEntityRepository + { + public function __construct(RegistryInterface $registry) + { + parent::__construct($registry, Product::class); + } - Take note of the ``setParameter()`` method. When working with Doctrine, - it's always a good idea to set any external values as "placeholders" - (``:price`` in the example above) as it prevents SQL injection attacks. + /** + * @param $price + * @return Product[] + */ + public function findAllGreaterThanPrice($price): array + { + // automatically knows to select Products + // the "p" is an alias you'll use in the rest of the query + $qb = $this->createQueryBuilder('p') + ->andWhere('p.price > :price') + ->setParameter('price', $price) + ->orderBy('p.price', 'ASC') + ->getQuery(); + + return $qb->execute(); + + // to get just one result: + // $product = $qb->setMaxResults(1)->getOneOrNullResult(); + } + } -The ``getResult()`` method returns an array of results. To get only one -result, you can use ``getOneOrNullResult()``:: +This uses Doctrine's `Query Builder`_: a very powerful and user-friendly way to +write custom queries. Now, you can call this method on the repository:: - $product = $query->setMaxResults(1)->getOneOrNullResult(); + // from inside a controller + $minPrice = 1000; -The DQL syntax is incredibly powerful, allowing you to easily join between -entities (the topic of :doc:`relations ` will be -covered later), group, etc. For more information, see the official -`Doctrine Query Language`_ documentation. + $products = $this->getDoctrine() + ->getRepository(Product::class) + ->findAllGreaterThanPrice($minPrice); -Querying for Objects Using Doctrine's Query Builder -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ... -Instead of writing a DQL string, you can use a helpful object called the -``QueryBuilder`` to build that string for you. This is useful when the actual query -depends on dynamic conditions, as your code soon becomes hard to read with -DQL as you start to concatenate strings:: +If you're in a :ref:`services-constructor-injection`, you can type-hint the +``ProductRepository`` class and inject it like normal. - $repository = $this->getDoctrine() - ->getRepository(Product::class); +For more details, see the `Query Builder`_ Documentation from Doctrine. - // createQueryBuilder() automatically selects FROM AppBundle:Product - // and aliases it to "p" - $query = $repository->createQueryBuilder('p') - ->where('p.price > :price') - ->setParameter('price', '19.99') - ->orderBy('p.price', 'ASC') - ->getQuery(); +Querying with DQL or SQL +------------------------ - $products = $query->getResult(); - // to get just one result: - // $product = $query->setMaxResults(1)->getOneOrNullResult(); +In addition to the query builder, you can also query with `Doctrine Query Language`_:: -The ``QueryBuilder`` object contains every method necessary to build your -query. By calling the ``getQuery()`` method, the query builder returns a -normal ``Query`` object, which can be used to get the result of the query. + // src/Repository/ProductRepository.php + // ... -For more information on Doctrine's Query Builder, consult Doctrine's -`Query Builder`_ documentation. + public function findAllGreaterThanPrice($price): array + { + $entityManager = $this->getEntityManager(); -Organizing Custom Queries into Repository Classes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + $query = $entityManager->createQuery( + 'SELECT p + FROM App\Entity\Product p + WHERE p.price > :price + ORDER BY p.price ASC' + )->setParameter('price', 1000); -All the queries in the previous sections were written directly in your controller. -But for organization, Doctrine provides special repository classes that allow you -to keep all your query logic in one, central place. + // returns an array of Product objects + return $query->execute(); + } -see :doc:`/doctrine/repository` for info. +Or directly with SQL if you need to:: -Configuration -------------- + // src/Repository/ProductRepository.php + // ... -Doctrine is highly configurable, though you probably won't ever need to worry -about most of its options. To find out more about configuring Doctrine, see -the Doctrine section of the :doc:`config reference `. + public function findAllGreaterThanPrice($price): array + { + $conn = $this->getEntityManager()->getConnection(); + + $sql = ' + SELECT * FROM product p + WHERE p.price > :price + ORDER BY p.price ASC + '; + $stmt = $conn->prepare($sql); + $stmt->execute(['price' => 1000]); + + // returns an array of arrays (i.e. a raw data set) + return $stmt->fetchAll(); + } -.. _doctrine-field-types: +With SQL, you will get back raw data, not objects (unless you use the `NativeQuery`_ +functionality). -Doctrine Field Types Reference ------------------------------- +Configuration +------------- -Doctrine comes with numerous field types available. Each of these -maps a PHP data type to a specific column type in whatever database you're -using. For each field type, the ``Column`` can be configured further, setting -the ``length``, ``nullable`` behavior, ``name`` and other options. To see a -list of all available types and more information, see Doctrine's -`Mapping Types documentation`_. +See the :doc:`Doctrine config reference `. Relationships and Associations ------------------------------ Doctrine provides all the functionality you need to manage database relationships -(also known as associations). For info, see :doc:`/doctrine/associations`. +(also known as associations), including ManyToOne, OneToMany, OneToOne and ManyToMany +relationships. -Final Thoughts --------------- +For info, see :doc:`/doctrine/associations`. -With Doctrine, you can focus on your *objects* and how they're used in your -application and worry about database persistence second. This is because -Doctrine allows you to use any PHP object to hold your data and relies on -mapping metadata information to map an object's data to a particular database -table. +Dummy Data Fixtures +------------------- -Doctrine has a lot more complex features to learn, like relationships, complex queries, -and event listeners. +Doctrine provides a library that allows you to programmatically load testing +data into your project (i.e. "fixture data"). For information, see +the "`DoctrineFixturesBundle`_" documentation. Learn more ---------- .. toctree:: :maxdepth: 1 - :glob: - doctrine/* + doctrine/associations + doctrine/common_extensions + doctrine/lifecycle_callbacks + doctrine/event_listeners_subscribers + doctrine/registration_form + doctrine/custom_dql_functions + doctrine/dbal + doctrine/multiple_entity_managers + doctrine/pdo_session_storage + doctrine/mongodb_session_storage + doctrine/resolve_target_entity + doctrine/reverse_engineering * `DoctrineFixturesBundle`_ -* `DoctrineMongoDBBundle`_ .. _`Doctrine`: http://www.doctrine-project.org/ +.. _`RFC 3986`: https://www.ietf.org/rfc/rfc3986.txt .. _`MongoDB`: https://www.mongodb.org/ -.. _`Basic Mapping Documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html +.. _`Doctrine's Mapping Types documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html .. _`Query Builder`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html .. _`Doctrine Query Language`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html .. _`Mapping Types Documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#property-mapping -.. _`Property Mapping`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#property-mapping .. _`Reserved SQL keywords documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words -.. _`Creating Classes for the Database`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#creating-classes-for-the-database .. _`DoctrineMongoDBBundle`: https://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html -.. _`migrations`: https://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html .. _`DoctrineFixturesBundle`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html -.. _`FrameworkExtraBundle documentation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html -.. _`newer utf8mb4 character set`: https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html .. _`Transactions and Concurrency`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html +.. _`DoctrineMigrationsBundle`: https://github.com/doctrine/DoctrineMigrationsBundle +.. _`NativeQuery`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html +.. _`SensioFrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html +.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html diff --git a/doctrine/associations.rst b/doctrine/associations.rst index ffec874dcc5..8c1c2e815c4 100644 --- a/doctrine/associations.rst +++ b/doctrine/associations.rst @@ -4,45 +4,140 @@ How to Work with Doctrine Associations / Relations ================================================== +There are **two** main relationship/association types: + +``ManyToOne`` / ``OneToMany`` + The most common relationship, mapped in the database with a simple foreign + key column (e.g. a ``category_id`` column on the ``product`` table). This is + actually just *one* association type, but seen from the two different *sides* + of the relation. + +``ManyToMany`` + Uses a join table and is needed when both sides of the relationship can have + many of the other side (e.g. "students" and "classes": each student is in many + classes, and each class has many students). + +First, you need to determine which relationship to use. If both sides of the relation +will contain many of the other side (e.g. "students" and "classes"), you need a +``ManyToMany`` relation. Otherwise, you likely need a ``ManyToOne``. + +.. tip:: + + There is also a OneToOne relationship (e.g. one User has one Profile and vice + versa). In practice, using this is similar to ``ManyToOne``. + +The ManyToOne / OneToMany Association +------------------------------------- + Suppose that each product in your application belongs to exactly one category. In this case, you'll need a ``Category`` class, and a way to relate a ``Product`` object to a ``Category`` object. -Start by creating the ``Category`` entity. Since you know that you'll eventually -need to persist category objects through Doctrine, you can let Doctrine create -the class for you. +Start by creating a ``Category`` entity with a ``name`` field: .. code-block:: terminal - $ php bin/console doctrine:generate:entity --no-interaction \ - --entity="AppBundle:Category" \ - --fields="name:string(255)" + $ php bin/console make:entity Category + + New property name (press to stop adding fields): + > name + + Field type (enter ? to see all types) [string]: + > string + + Field length [255]: + > 255 -This command generates the ``Category`` entity for you, with an ``id`` field, -a ``name`` field and the associated getter and setter functions. + Can this field be null in the database (nullable) (yes/no) [no]: + > no -Relationship Mapping Metadata ------------------------------ + New property name (press to stop adding fields): + > + (press enter again to finish) + +This will generate your new entity class:: + + // src/Entity/Category.php + // ... -In this example, each category can be associated with *many* products, while + class Category + { + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; + + /** + * @ORM\Column(type="string") + */ + private $name; + + // ... getters and setters + } + +Mapping the ManyToOne Relationship +---------------------------------- + +In this example, each category can be associated with *many* products. But, each product can be associated with only *one* category. This relationship can be summarized as: *many* products to *one* category (or equivalently, *one* category to *many* products). From the perspective of the ``Product`` entity, this is a many-to-one relationship. From the perspective of the ``Category`` entity, this is a one-to-many relationship. -This is important, because the relative nature of the relationship determines -which mapping metadata to use. It also determines which class *must* hold -a reference to the other class. -To relate the ``Product`` and ``Category`` entities, simply create a ``category`` -property on the ``Product`` class, annotated as follows: +To map this, first create a ``category`` property on the ``Product`` class with +the ``ManyToOne`` annotation. You can do this by hand, or by using the ``make:entity`` +command, which will ask you several questions about your relationship. If you're +not sure of the answer, don't worry! You can always change the settings later: + +.. code-block:: terminal + + $ php bin/console make:entity + + Class name of the entity to create or update (e.g. BraveChef): + > Product + + New property name (press to stop adding fields): + > category + + Field type (enter ? to see all types) [string]: + > relation + + What class should this entity be related to?: + > Category + + Relation type? [ManyToOne, OneToMany, ManyToMany, OneToOne]: + > ManyToOne + + Is the Product.category property allowed to be null (nullable)? (yes/no) [yes]: + > no + + Do you want to add a new property to Category so that you can access/update + Product objects from it - e.g. $category->getProducts()? (yes/no) [yes]: + > yes + + New field name inside Category [products]: + > products + + Do you want to automatically delete orphaned App\Entity\Product objects + (orphanRemoval)? (yes/no) [no]: + > no + + New property name (press to stop adding fields): + > + (press enter again to finish) + +This made changes to *two* changes. First, added a new ``category`` property to +the ``Product`` entity (and getter & setter methods): .. configuration-block:: .. code-block:: php-annotations - // src/AppBundle/Entity/Product.php + // src/Entity/Product.php // ... class Product @@ -50,71 +145,81 @@ property on the ``Product`` class, annotated as follows: // ... /** - * @ORM\ManyToOne(targetEntity="Category", inversedBy="products") - * @ORM\JoinColumn(name="category_id", referencedColumnName="id") + * @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products") + * @ORM\JoinColumn(nullable=false) */ private $category; + + public function getCategory(): ?Category + { + return $this->category; + } + + public function setCategory(?Category $category): self + { + $this->category = $category; + + return $this; + } } .. code-block:: yaml - # src/AppBundle/Resources/config/doctrine/Product.orm.yml - AppBundle\Entity\Product: + # src/Resources/config/doctrine/Product.orm.yml + App\Entity\Product: type: entity # ... manyToOne: category: - targetEntity: Category + targetEntity: App\Entity\Category inversedBy: products joinColumn: - name: category_id - referencedColumnName: id + nullable: false .. code-block:: xml - + - + - - + target-entity="App\Entity\Category" + inversed-by="products"> + -This many-to-one mapping is critical. It tells Doctrine to use the ``category_id`` +This ``ManyToOne`` mapping is required. It tells Doctrine to use the ``category_id`` column on the ``product`` table to relate each record in that table with a record in the ``category`` table. -Next, since a single ``Category`` object will relate to many ``Product`` -objects, a ``products`` property can be added to the ``Category`` class -to hold those associated objects. +Next, since a *one* ``Category`` object will relate to *many* ``Product`` objects, +the ``make:entity`` command *also* added a ``products`` property to the ``Category`` +class that will hold these objects:: .. configuration-block:: .. code-block:: php-annotations - // src/AppBundle/Entity/Category.php + // src/Entity/Category.php // ... use Doctrine\Common\Collections\ArrayCollection; + use Doctrine\Common\Collections\Collection; class Category { // ... /** - * @ORM\OneToMany(targetEntity="Product", mappedBy="category") + * @ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="category") */ private $products; @@ -122,35 +227,45 @@ to hold those associated objects. { $this->products = new ArrayCollection(); } + + /** + * @return Collection|Product[] + */ + public function getProducts(): Collection + { + return $this->products; + } + + // addProduct() and removeProduct() were also added } .. code-block:: yaml - # src/AppBundle/Resources/config/doctrine/Category.orm.yml - AppBundle\Entity\Category: + # src/Resources/config/doctrine/Category.orm.yml + App\Entity\Category: type: entity # ... oneToMany: products: - targetEntity: Product + targetEntity: App\Entity\Product mappedBy: category # Don't forget to initialize the collection in # the __construct() method of the entity .. code-block:: xml - + - + + - AppBundle\DQL\StringFunction - AppBundle\DQL\SecondStringFunction - AppBundle\DQL\NumericFunction - AppBundle\DQL\DatetimeFunction + App\DQL\StringFunction + App\DQL\SecondStringFunction + App\DQL\NumericFunction + App\DQL\DatetimeFunction @@ -52,11 +52,11 @@ In Symfony, you can register your custom DQL functions as follows: .. code-block:: php - // app/config/config.php - use AppBundle\DQL\StringFunction; - use AppBundle\DQL\SecondStringFunction; - use AppBundle\DQL\NumericFunction; - use AppBundle\DQL\DatetimeFunction; + // config/packages/doctrine.php + use App\DQL\StringFunction; + use App\DQL\SecondStringFunction; + use App\DQL\NumericFunction; + use App\DQL\DatetimeFunction; $container->loadFromExtension('doctrine', array( 'orm' => array( @@ -86,7 +86,7 @@ In Symfony, you can register your custom DQL functions as follows: .. code-block:: yaml - # app/config/config.yml + # config/packages/doctrine.yaml doctrine: orm: # ... @@ -95,11 +95,11 @@ In Symfony, you can register your custom DQL functions as follows: # Place your functions here dql: datetime_functions: - test_datetime: AppBundle\DQL\DatetimeFunction + test_datetime: App\DQL\DatetimeFunction .. code-block:: xml - # app/config/config.xml + - AppBundle\DQL\DatetimeFunction + App\DQL\DatetimeFunction @@ -127,8 +127,8 @@ In Symfony, you can register your custom DQL functions as follows: .. code-block:: php - // app/config/config.php - use AppBundle\DQL\DatetimeFunction; + // config/packages/doctrine.php + use App\DQL\DatetimeFunction; $container->loadFromExtension('doctrine', array( 'doctrine' => array( diff --git a/doctrine/dbal.rst b/doctrine/dbal.rst index cb27a1b8a70..269cd529691 100644 --- a/doctrine/dbal.rst +++ b/doctrine/dbal.rst @@ -21,67 +21,27 @@ makes it easy to execute queries and perform other database actions. Read the official Doctrine `DBAL Documentation`_ to learn all the details and capabilities of Doctrine's DBAL library. -To get started, configure the database connection parameters: +First, install the Doctrine bundle: -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - doctrine: - dbal: - driver: pdo_mysql - dbname: Symfony - user: root - password: null - charset: UTF8 - server_version: 5.6 +.. code-block:: terminal - .. code-block:: xml - - - - + composer require doctrine/doctrine-bundle - - - +Then configure the ``DATABASE_URL`` environment variable in ``.env``: - +.. code-block:: text - .. code-block:: php + # .env - // app/config/config.php - $container->loadFromExtension('doctrine', array( - 'dbal' => array( - 'driver' => 'pdo_mysql', - 'dbname' => 'Symfony', - 'user' => 'root', - 'password' => null, - 'charset' => 'UTF8', - 'server_version' => '5.6', - ), - )); + # customize this line! + DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" -For full DBAL configuration options, or to learn how to configure multiple -connections, see :ref:`reference-dbal-configuration`. +Further things can be configured in ``config/packages/doctrine.yaml``. For the full +DBAL configuration options, or to learn how to configure multiple connections, +see :ref:`reference-dbal-configuration`. -You can then access the Doctrine DBAL connection by accessing the -``database_connection`` service:: +You can then access the Doctrine DBAL connection by autowiring the ``Connection`` +object:: use Doctrine\DBAL\Driver\Connection; @@ -95,6 +55,8 @@ You can then access the Doctrine DBAL connection by accessing the } } +This will pass you the ``database_connection`` service. + Registering custom Mapping Types -------------------------------- @@ -106,16 +68,16 @@ mapping types, read Doctrine's `Custom Mapping Types`_ section of their document .. code-block:: yaml - # app/config/config.yml + # config/packages/doctrine.yaml doctrine: dbal: types: - custom_first: AppBundle\Type\CustomFirst - custom_second: AppBundle\Type\CustomSecond + custom_first: App\Type\CustomFirst + custom_second: App\Type\CustomSecond .. code-block:: xml - + - - + + .. code-block:: php - // app/config/config.php - use AppBundle\Type\CustomFirst; - use AppBundle\Type\CustomSecond; + // config/packages/doctrine.php + use App\Type\CustomFirst; + use App\Type\CustomSecond; $container->loadFromExtension('doctrine', array( 'dbal' => array( @@ -161,7 +123,7 @@ mapping type: .. code-block:: yaml - # app/config/config.yml + # config/packages/doctrine.yaml doctrine: dbal: mapping_types: @@ -169,7 +131,7 @@ mapping type: .. code-block:: xml - + loadFromExtension('doctrine', array( 'dbal' => array( 'mapping_types' => array( diff --git a/doctrine/event_listeners_subscribers.rst b/doctrine/event_listeners_subscribers.rst index de4cc206547..694c9dbd1f8 100644 --- a/doctrine/event_listeners_subscribers.rst +++ b/doctrine/event_listeners_subscribers.rst @@ -36,13 +36,13 @@ managers that use this connection. services: # ... - AppBundle\EventListener\SearchIndexer: + App\EventListener\SearchIndexer: tags: - { name: doctrine.event_listener, event: postPersist } - AppBundle\EventListener\SearchIndexer2: + App\EventListener\SearchIndexer2: tags: - { name: doctrine.event_listener, event: postPersist, connection: default } - AppBundle\EventListener\SearchIndexerSubscriber: + App\EventListener\SearchIndexerSubscriber: tags: - { name: doctrine.event_subscriber, connection: default } @@ -54,13 +54,13 @@ managers that use this connection. - + - + - + @@ -68,9 +68,9 @@ managers that use this connection. .. code-block:: php - use AppBundle\EventListener\SearchIndexer; - use AppBundle\EventListener\SearchIndexer2; - use AppBundle\EventListener\SearchIndexerSubscriber; + use App\EventListener\SearchIndexer; + use App\EventListener\SearchIndexer2; + use App\EventListener\SearchIndexerSubscriber; $container->autowire(SearchIndexer::class) ->addTag('doctrine.event_listener', array('event' => 'postPersist')) @@ -92,11 +92,11 @@ In the previous example, a ``SearchIndexer`` service was configured as a Doctrin listener on the event ``postPersist``. The class behind that service must have a ``postPersist()`` method, which will be called when the event is dispatched:: - // src/AppBundle/EventListener/SearchIndexer.php - namespace AppBundle\EventListener; + // src/EventListener/SearchIndexer.php + namespace App\EventListener; use Doctrine\ORM\Event\LifecycleEventArgs; - use AppBundle\Entity\Product; + use App\Entity\Product; class SearchIndexer { @@ -136,13 +136,13 @@ Creating the Subscriber Class A Doctrine event subscriber must implement the ``Doctrine\Common\EventSubscriber`` interface and have an event method for each event it subscribes to:: - // src/AppBundle/EventListener/SearchIndexerSubscriber.php - namespace AppBundle\EventListener; + // src/EventListener/SearchIndexerSubscriber.php + namespace App\EventListener; use Doctrine\Common\EventSubscriber; // for Doctrine < 2.4: use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\Common\Persistence\Event\LifecycleEventArgs; - use AppBundle\Entity\Product; + use App\Entity\Product; class SearchIndexerSubscriber implements EventSubscriber { @@ -207,8 +207,7 @@ to the tag like so: .. code-block:: yaml services: - my.listener: - class: AppBundle\EventListener\SearchIndexer + App\EventListener\SearchIndexer: tags: - { name: doctrine.event_listener, event: postPersist, lazy: true } @@ -219,7 +218,7 @@ to the tag like so: xmlns:doctrine="http://symfony.com/schema/dic/doctrine"> - + @@ -227,10 +226,10 @@ to the tag like so: .. code-block:: php - use AppBundle\EventListener\SearchIndexer; + use App\EventListener\SearchIndexer; $container - ->register('my.listener', SearchIndexer::class) + ->autowire(SearchIndexer::class) ->addTag('doctrine.event_listener', array('event' => 'postPersist', 'lazy' => 'true')) ; diff --git a/doctrine/lifecycle_callbacks.rst b/doctrine/lifecycle_callbacks.rst index a6535a9c749..42344c8098d 100644 --- a/doctrine/lifecycle_callbacks.rst +++ b/doctrine/lifecycle_callbacks.rst @@ -32,7 +32,7 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: php-annotations - // src/AppBundle/Entity/Product.php + // src/Entity/Product.php /** * @ORM\PrePersist @@ -44,8 +44,8 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: yaml - # src/AppBundle/Resources/config/doctrine/Product.orm.yml - AppBundle\Entity\Product: + # config/doctrine/Product.orm.yml + App\Entity\Product: type: entity # ... lifecycleCallbacks: @@ -53,14 +53,14 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: xml - + - + diff --git a/doctrine/mapping_model_classes.rst b/doctrine/mapping_model_classes.rst deleted file mode 100644 index 340a3005cd0..00000000000 --- a/doctrine/mapping_model_classes.rst +++ /dev/null @@ -1,149 +0,0 @@ -.. index:: - single: Doctrine; Mapping Model classes - -How to Provide Model Classes for several Doctrine Implementations -================================================================= - -When building a bundle that could be used not only with Doctrine ORM but -also the CouchDB ODM, MongoDB ODM or PHPCR ODM, you should still only -write one model class. The Doctrine bundles provide a compiler pass to -register the mappings for your model classes. - -.. note:: - - For non-reusable bundles, the easiest option is to put your model classes - in the default locations: ``Entity`` for the Doctrine ORM or ``Document`` - for one of the ODMs. For reusable bundles, rather than duplicate model classes - just to get the auto-mapping, use the compiler pass. - -In your bundle class, write the following code to register the compiler pass. -This one is written for the CmfRoutingBundle, so parts of it will need to -be adapted for your case:: - - use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass; - use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\DoctrineMongoDBMappingsPass; - use Doctrine\Bundle\CouchDBBundle\DependencyInjection\Compiler\DoctrineCouchDBMappingsPass; - use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass; - use Symfony\Cmf\RoutingBundle\Model; - - class CmfRoutingBundle extends Bundle - { - public function build(ContainerBuilder $container) - { - parent::build($container); - // ... - - $modelDirectory = realpath(__DIR__.'/Resources/config/doctrine/model'); - $mappings = array( - $modelDirectory => Model::class, - ); - - if (class_exists(DoctrineOrmMappingsPass::class)) { - $container->addCompilerPass( - DoctrineOrmMappingsPass::createXmlMappingDriver( - $mappings, - array('cmf_routing.model_manager_name'), - 'cmf_routing.backend_type_orm', - array('CmfRoutingBundle' => Model::class) - )); - } - - if (class_exists(DoctrineMongoDBMappingsPass::class)) { - $container->addCompilerPass( - DoctrineMongoDBMappingsPass::createXmlMappingDriver( - $mappings, - array('cmf_routing.model_manager_name'), - 'cmf_routing.backend_type_mongodb', - array('CmfRoutingBundle' => Model::class) - )); - } - - if (class_exists(DoctrineCouchDBMappingsPass::class)) { - $container->addCompilerPass( - DoctrineCouchDBMappingsPass::createXmlMappingDriver( - $mappings, - array('cmf_routing.model_manager_name'), - 'cmf_routing.backend_type_couchdb', - array('CmfRoutingBundle' => Model::class) - )); - } - - if (class_exists(DoctrinePhpcrMappingsPass::class)) { - $container->addCompilerPass( - DoctrinePhpcrMappingsPass::createXmlMappingDriver( - $mappings, - array('cmf_routing.model_manager_name'), - 'cmf_routing.backend_type_phpcr', - array('CmfRoutingBundle' => Model::class) - )); - } - } - } - -Note the :phpfunction:`class_exists` check. This is crucial, as you do not want your -bundle to have a hard dependency on all Doctrine bundles but let the user -decide which to use. - -The compiler pass provides factory methods for all drivers provided by Doctrine: -Annotations, XML, Yaml, PHP and StaticPHP. The arguments are: - -* A map/hash of absolute directory path to namespace; -* An array of container parameters that your bundle uses to specify the name of - the Doctrine manager that it is using. In the example above, the CmfRoutingBundle - stores the manager name that's being used under the ``cmf_routing.model_manager_name`` - parameter. The compiler pass will append the parameter Doctrine is using - to specify the name of the default manager. The first parameter found is - used and the mappings are registered with that manager; -* An optional container parameter name that will be used by the compiler - pass to determine if this Doctrine type is used at all. This is relevant if - your user has more than one type of Doctrine bundle installed, but your - bundle is only used with one type of Doctrine; -* A map/hash of aliases to namespace. This should be the same convention used - by Doctrine auto-mapping. In the example above, this allows the user to call - ``$om->getRepository('CmfRoutingBundle:Route')``. - -.. note:: - - The factory method is using the ``SymfonyFileLocator`` of Doctrine, meaning - it will only see XML and YML mapping files if they do not contain the - full namespace as the filename. This is by design: the ``SymfonyFileLocator`` - simplifies things by assuming the files are just the "short" version - of the class as their filename (e.g. ``BlogPost.orm.xml``) - - If you also need to map a base class, you can register a compiler pass - with the ``DefaultFileLocator`` like this. This code is taken from the - ``DoctrineOrmMappingsPass`` and adapted to use the ``DefaultFileLocator`` - instead of the ``SymfonyFileLocator``:: - - use Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator; - use Doctrine\ORM\Mapping\Driver\XmlDriver; - use AppBundle\Model; - - // ... - private function buildMappingCompilerPass() - { - $fileLocator = new Definition(DefaultFileLocator::class, array( - array(realpath(__DIR__ . '/Resources/config/doctrine-base')), - '.orm.xml' - )); - $driver = new Definition(XmlDriver::class, array($fileLocator)); - - return new DoctrineOrmMappingsPass( - $driver, - array(Model::class), - array('your_bundle.manager_name'), - 'your_bundle.orm_enabled' - ); - } - - Note that you do not need to provide a namespace alias unless your users are - expected to ask Doctrine for the base classes. - - Now place your mapping file into ``/Resources/config/doctrine-base`` with the - fully qualified class name, separated by ``.`` instead of ``\``, for example - ``Other.Namespace.Model.Name.orm.xml``. You may not mix the two as otherwise - the ``SymfonyFileLocator`` will get confused. - - Adjust accordingly for the other Doctrine implementations. - -.. _`CouchDB Mapping Compiler Pass pull request`: https://github.com/doctrine/DoctrineCouchDBBundle/pull/27 diff --git a/doctrine/mongodb_session_storage.rst b/doctrine/mongodb_session_storage.rst index fcfecfe3dd2..2435529f11a 100644 --- a/doctrine/mongodb_session_storage.rst +++ b/doctrine/mongodb_session_storage.rst @@ -8,165 +8,24 @@ in a multi-webserver environment. Symfony has a built-in solution for NoSQL database session storage called :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler`. -MongoDB is an open-source document database that provides high performance, -high availability and automatic scaling. This article assumes that you have -already `installed and configured a MongoDB server`_. To use it, you just -need to change/add some parameters in the main configuration file: +To use this, you will need to: -.. configuration-block:: +A) Register a ``MongoDbSessionHandler`` service; - .. code-block:: yaml +B) Configure this under ``framework.session.handler_id`` configuration. - # app/config/config.yml - framework: - session: - # ... - handler_id: session.handler.mongo - cookie_lifetime: 2592000 # optional, it is set to 30 days here - gc_maxlifetime: 2592000 # optional, it is set to 30 days here - - services: - # ... - mongo_client: - class: MongoClient - # if using a username and password - arguments: ['mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017'] - # if not using a username and password - arguments: ['mongodb://%mongodb_host%:27017'] - session.handler.mongo: - class: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler - arguments: ['@mongo_client', '%mongo.session.options%'] - - .. code-block:: xml - - - - - - - - - - - - - - - mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017 - - - mongodb://%mongodb_host%:27017 - - - - mongo_client - %mongo.session.options% - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Reference; - use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - - $container->loadFromExtension('framework', array( - 'session' => array( - // ... - 'handler_id' => 'session.handler.mongo', - 'cookie_lifetime' => 2592000, // optional, it is set to 30 days here - 'gc_maxlifetime' => 2592000, // optional, it is set to 30 days here - ), - )); - - $container->register('mongo_client', \MongoClient::class) - ->setArguments(array( - // if using a username and password - array('mongodb://%mongodb_username%:%mongodb_password%@%mongodb_host%:27017'), - // if not using a username and password - array('mongodb://%mongodb_host%:27017'), - )); - - $container->register('session.handler.mongo', MongoDbSessionHandler::class) - ->setArguments(array( - new Reference('mongo_client'), - '%mongo.session.options%', - )); - -The parameters used above should be defined somewhere in your application, often in your main -parameters configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/parameters.yml - parameters: - # ... - mongo.session.options: - database: session_db # your MongoDB database name - collection: session # your MongoDB collection name - mongodb_host: 1.2.3.4 # your MongoDB server's IP - mongodb_username: my_username - mongodb_password: my_password - - .. code-block:: xml - - - - - - - - session_db - - session - - - 1.2.3.4 - my_username - my_password - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Reference; - - $container->setParameter('mongo.session.options', array( - 'database' => 'session_db', // your MongoDB database name - 'collection' => 'session', // your MongoDB collection name - )); - $container->setParameter('mongodb_host', '1.2.3.4'); // your MongoDB server's IP - $container->setParameter('mongodb_username', 'my_username'); - $container->setParameter('mongodb_password', 'my_password'); +To see how to configure a similar handler, see :doc:`/doctrine/pdo_session_storage`. Setting Up the MongoDB Collection --------------------------------- -Because MongoDB uses dynamic collection schemas, you do not need to do anything to initialize your -session collection. However, you may want to add an index to improve garbage collection performance. -From the `MongoDB shell`_: +You do not need to do anything to initialize your session collection. However, you +may want to add an index to improve garbage collection performance. From the +`MongoDB shell`_: .. code-block:: javascript use session_db db.session.ensureIndex( { "expires_at": 1 }, { expireAfterSeconds: 0 } ) -.. _installed and configured a MongoDB server: http://docs.mongodb.org/manual/installation/ .. _MongoDB shell: http://docs.mongodb.org/v2.2/tutorial/getting-started-with-the-mongo-shell/ diff --git a/doctrine/multiple_entity_managers.rst b/doctrine/multiple_entity_managers.rst index 8f43b493660..4b09346871b 100644 --- a/doctrine/multiple_entity_managers.rst +++ b/doctrine/multiple_entity_managers.rst @@ -28,6 +28,7 @@ The following configuration code shows how you can configure two entity managers .. code-block:: yaml + # config/packages/doctrine.yaml doctrine: dbal: default_connection: default @@ -55,15 +56,25 @@ The following configuration code shows how you can configure two entity managers default: connection: default mappings: - AppBundle: ~ - AcmeStoreBundle: ~ + Main: + is_bundle: false + type: annotation + dir: '%kernel.project_dir%/src/Entity/Main' + prefix: 'App\Entity\Main' + alias: Main customer: connection: customer mappings: - AcmeCustomerBundle: ~ + Customer: + is_bundle: false + type: annotation + dir: '%kernel.project_dir%/src/Entity/Customer' + prefix: 'App\Entity\Customer' + alias: Customer .. code-block:: xml + - - + - + @@ -111,6 +135,7 @@ The following configuration code shows how you can configure two entity managers .. code-block:: php + // config/packages/doctrine.php $container->loadFromExtension('doctrine', array( 'dbal' => array( 'default_connection' => 'default', @@ -142,14 +167,25 @@ The following configuration code shows how you can configure two entity managers 'default' => array( 'connection' => 'default', 'mappings' => array( - 'AppBundle' => null, - 'AcmeStoreBundle' => null, + 'Main' => array( + is_bundle => false, + type => 'annotation', + dir => '%kernel.project_dir%/src/Entity/Main', + prefix => 'App\Entity\Main', + alias => 'Main', + ) ), ), 'customer' => array( 'connection' => 'customer', 'mappings' => array( - 'AcmeCustomerBundle' => null, + 'Customer' => array( + is_bundle => false, + type => 'annotation', + dir => '%kernel.project_dir%/src/Entity/Customer', + prefix => 'App\Entity\Customer', + alias => 'Customer', + ) ), ), ), @@ -158,8 +194,8 @@ The following configuration code shows how you can configure two entity managers In this case, you've defined two entity managers and called them ``default`` and ``customer``. The ``default`` entity manager manages entities in the -AppBundle and AcmeStoreBundle, while the ``customer`` entity manager manages -entities in the AcmeCustomerBundle. You've also defined two connections, one +``src/Entity/Main`` directory, while the ``customer`` entity manager manages +entities in ``src/Entity/Customer``. You've also defined two connections, one for each entity manager. .. note:: @@ -178,26 +214,31 @@ When working with multiple connections to create your databases: # Play only with "customer" connection $ php bin/console doctrine:database:create --connection=customer -When working with multiple entity managers to update your schema: +When working with multiple entity managers to generate migrations: .. code-block:: terminal # Play only with "default" mappings - $ php bin/console doctrine:schema:update --force + $ php bin/console doctrine:migrations:diff + $ php bin/console doctrine:migrations:migrate # Play only with "customer" mappings - $ php bin/console doctrine:schema:update --force --em=customer + $ php bin/console doctrine:migrations:diff --em=customer + $ php bin/console doctrine:migrations:migrate --em=customer If you *do* omit the entity manager's name when asking for it, the default entity manager (i.e. ``default``) is returned:: // ... + use Doctrine\ORM\EntityManagerInterface; + class UserController extends Controller { - public function indexAction() + public function indexAction(EntityManagerInterface $entityManager) { - // All 3 return the "default" entity manager + // These methods also return the default entity manager, but it's preferred + // to get it by injecting EntityManagerInterface in the action method $entityManager = $this->getDoctrine()->getManager(); $entityManager = $this->getDoctrine()->getManager('default'); $entityManager = $this->get('doctrine.orm.default_entity_manager'); diff --git a/doctrine/pdo_session_storage.rst b/doctrine/pdo_session_storage.rst index e78df56e758..cb2c96ddec3 100644 --- a/doctrine/pdo_session_storage.rst +++ b/doctrine/pdo_session_storage.rst @@ -17,19 +17,22 @@ To use it, first register a new handler service: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml services: # ... Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: - public: false arguments: - 'mysql:dbname=mydatabase' - { db_username: myuser, db_password: mypassword } + # If you're using Doctrine & want to re-use that connection, then: + # comment-out the above 2 lines and uncomment the line below + # - !service { class: PDO, factory: 'database_connection:getWrappedConnection' } + .. code-block:: xml - + register(PdoSessionHandler::class) + $storageDefinition = $container->autowire(PdoSessionHandler::class) ->setArguments(array( 'mysql:dbname=mydatabase', array('db_username' => 'myuser', 'db_password' => 'mypassword') @@ -67,7 +70,7 @@ Next, tell Symfony to use your service as the session handler: .. code-block:: yaml - # app/config/config.yml + # config/packages/framework.yaml framework: session: # ... @@ -75,7 +78,7 @@ Next, tell Symfony to use your service as the session handler: .. code-block:: xml - + @@ -83,7 +86,7 @@ Next, tell Symfony to use your service as the session handler: .. code-block:: php - // app/config/config.php + // config/packages/framework.php use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; // ... @@ -106,19 +109,18 @@ a second array argument to ``PdoSessionHandler``: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml services: # ... Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: - public: false arguments: - 'mysql:dbname=mydatabase' - { db_table: sessions, db_username: myuser, db_password: mypassword } .. code-block:: xml - + register(PdoSessionHandler::class) + $container->autowire(PdoSessionHandler::class) ->setArguments(array( 'mysql:dbname=mydatabase', array('db_table' => 'sessions', 'db_username' => 'myuser', 'db_password' => 'mypassword') @@ -168,59 +170,6 @@ These are parameters that you can configure: ``db_lifetime_col`` (default ``sess_lifetime``): The name of the lifetime column in your session table (INTEGER). -Sharing your Database Connection Information --------------------------------------------- - -With the given configuration, the database connection settings are defined for -the session storage connection only. This is OK when you use a separate -database for the session data. - -But if you'd like to store the session data in the same database as the rest -of your project's data, you can use the connection settings from the -``parameters.yml`` file by referencing the database-related parameters defined there: - -.. configuration-block:: - - .. code-block:: yaml - - services: - # ... - - Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: - public: false - arguments: - - 'mysql:host=%database_host%;port=%database_port%;dbname=%database_name%' - - { db_username: '%database_user%', db_password: '%database_password%' } - - .. code-block:: xml - - - - - - - mysql:host=%database_host%;port=%database_port%;dbname=%database_name% - - %database_user% - %database_password% - - - - - - .. code-block:: php - - // ... - $container->register(PdoSessionHandler::class) - ->setArguments(array( - 'mysql:host=%database_host%;port=%database_port%;dbname=%database_name%', - array('db_username' => '%database_user%', 'db_password' => '%database_password%') - )) - ; - .. _example-sql-statements: Preparing the Database to Store Sessions @@ -240,6 +189,19 @@ to set up this table for you according to the database engine used:: If you prefer to set up the table yourself, these are some examples of the SQL statements you may use according to your specific database engine. +A great way to run this on production is to generate an empty migration, and then +add this SQL inside: + +.. code-block:: terminal + + $ php bin/console doctrine:migrations:generate + +Find the correct SQL below and put it inside that file. Then execute it with: + +.. code-block:: terminal + + $ php bin/console doctrine:migrations:migrate + MySQL ~~~~~ diff --git a/doctrine/registration_form.rst b/doctrine/registration_form.rst index dc428d99090..49b5d33f3c1 100644 --- a/doctrine/registration_form.rst +++ b/doctrine/registration_form.rst @@ -10,6 +10,12 @@ Creating a registration form is pretty easy - it *really* means just creating a form that will update some ``User`` model object (a Doctrine entity in this example) and then save it. +First, make sure you have all the dependencies you need installed: + +.. code-block:: terminal + + $ composer require doctrine form security validator + .. tip:: The popular `FOSUserBundle`_ provides a registration form, reset password @@ -39,8 +45,8 @@ Your ``User`` entity will probably at least have the following fields: With some validation added, your class may look something like this:: - // src/AppBundle/Entity/User.php - namespace AppBundle\Entity; + // src/Entity/User.php + namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -141,7 +147,7 @@ With some validation added, your class may look something like this:: } The :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` requires -a few other methods and your ``security.yml`` file needs to be configured +a few other methods and your ``security.yaml`` file needs to be configured properly to work with the ``User`` entity. For a more complete example, see the :ref:`Entity Provider ` article. @@ -167,10 +173,10 @@ Create a Form for the Entity Next, create the form for the ``User`` entity:: - // src/AppBundle/Form/UserType.php - namespace AppBundle\Form; + // src/Form/UserType.php + namespace App\Form; - use AppBundle\Entity\User; + use App\Entity\User; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -217,11 +223,11 @@ Next, you need a controller to handle the form rendering and submission. If the form is submitted, the controller performs the validation and saves the data into the database:: - // src/AppBundle/Controller/RegistrationController.php - namespace AppBundle\Controller; + // src/Controller/RegistrationController.php + namespace App\Controller; - use AppBundle\Form\UserType; - use AppBundle\Entity\User; + use App\Form\UserType; + use App\Entity\User; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; @@ -271,14 +277,14 @@ encoder in the security configuration: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: encoders: - AppBundle\Entity\User: bcrypt + App\Entity\User: bcrypt .. code-block:: xml - + - bcrypt + bcrypt .. code-block:: php - // app/config/security.php - use AppBundle\Entity\User; + // config/packages/security.php + use App\Entity\User; $container->loadFromExtension('security', array( 'encoders' => array( @@ -304,76 +310,20 @@ encoder in the security configuration: In this case the recommended ``bcrypt`` algorithm is used. If needed, check out the :ref:`user password encoding ` article. -.. note:: - - If you decide to NOT use annotation routing (shown above), then you'll - need to create a route to this controller: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/routing.yml - user_registration: - path: /register - defaults: { _controller: AppBundle:Registration:register } - - .. code-block:: xml - - - - - - - AppBundle:Registration:register - - - - .. code-block:: php - - // app/config/routing.php - use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; - - $routes = new RouteCollection(); - $routes->add('user_registration', new Route('/register', array( - '_controller' => 'AppBundle:Registration:register', - ))); - - return $routes; - Next, create the template: -.. configuration-block:: - - .. code-block:: html+twig - - {# app/Resources/views/registration/register.html.twig #} - - {{ form_start(form) }} - {{ form_row(form.username) }} - {{ form_row(form.email) }} - {{ form_row(form.plainPassword.first) }} - {{ form_row(form.plainPassword.second) }} - - - {{ form_end(form) }} - - .. code-block:: html+php - - +.. code-block:: html+twig - start($form) ?> - row($form['username']) ?> - row($form['email']) ?> + {# templates/registration/register.html.twig #} - row($form['plainPassword']['first']) ?> - row($form['plainPassword']['second']) ?> + {{ form_start(form) }} + {{ form_row(form.username) }} + {{ form_row(form.email) }} + {{ form_row(form.plainPassword.first) }} + {{ form_row(form.plainPassword.second) }} - - end($form) ?> + + {{ form_end(form) }} See :doc:`/form/form_customization` for more details. @@ -385,7 +335,8 @@ your database schema using this command: .. code-block:: terminal - $ php bin/console doctrine:schema:update --force + $ php bin/console doctrine:migrations:diff + $ php bin/console doctrine:migrations:migrate That's it! Head to ``/register`` to try things out! @@ -398,7 +349,7 @@ If you want your users to login via email and you don't need a username, then yo can remove it from your ``User`` entity entirely. Instead, make ``getUsername()`` return the ``email`` property:: - // src/AppBundle/Entity/User.php + // src/Entity/User.php // ... class User implements UserInterface @@ -413,7 +364,7 @@ return the ``email`` property:: // ... } -Next, just update the ``providers`` section of your ``security.yml`` file +Next, just update the ``providers`` section of your ``security.yaml`` file so that Symfony knows how to load your users via the ``email`` property on login. See :ref:`authenticating-someone-with-a-custom-entity-provider`. @@ -428,7 +379,7 @@ that you'll never need. To do this, add a ``termsAccepted`` field to your form, but set its :ref:`mapped ` option to ``false``:: - // src/AppBundle/Form/UserType.php + // src/Form/UserType.php // ... use Symfony\Component\Validator\Constraints\IsTrue; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; diff --git a/doctrine/repository.rst b/doctrine/repository.rst deleted file mode 100644 index 2e8c87a46c5..00000000000 --- a/doctrine/repository.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. index:: - single: Doctrine; Custom Repository Class - -How to Create custom Repository Classes -======================================= - -In the previous sections, you began constructing and using more complex queries -from inside a controller. In order to isolate, reuse and test these queries, -it's a good practice to create a custom repository class for your entity. -Methods containing your query logic can then be stored in this class. - -To do this, add the repository class name to your entity's mapping definition: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/AppBundle/Entity/Product.php - namespace AppBundle\Entity; - - use Doctrine\ORM\Mapping as ORM; - - /** - * @ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository") - */ - class Product - { - //... - } - - .. code-block:: yaml - - # src/AppBundle/Resources/config/doctrine/Product.orm.yml - AppBundle\Entity\Product: - type: entity - repositoryClass: AppBundle\Repository\ProductRepository - # ... - - .. code-block:: xml - - - - - - - - - - - -Then, create an empty ``AppBundle\Repository\ProductRepository`` class extending -from ``Doctrine\ORM\EntityRepository``. - -Next, add a new method - ``findAllOrderedByName()`` - to the newly-generated -``ProductRepository`` class. This method will query for all the ``Product`` -entities, ordered alphabetically by name:: - - // src/AppBundle/Repository/ProductRepository.php - namespace AppBundle\Repository; - - use Doctrine\ORM\EntityRepository; - - class ProductRepository extends EntityRepository - { - public function findAllOrderedByName() - { - return $this->getEntityManager() - ->createQuery( - 'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC' - ) - ->getResult(); - } - } - -.. tip:: - - The entity manager can be accessed via ``$this->getEntityManager()`` - from inside the repository. - -You can use this new method just like the default finder methods of the repository:: - - use AppBundle\Entity\Product; - // ... - - public function listAction() - { - $products = $this->getDoctrine() - ->getRepository(Product::class) - ->findAllOrderedByName(); - } - -.. note:: - - When using a custom repository class, you still have access to the default - finder methods such as ``find()`` and ``findAll()``. diff --git a/doctrine/resolve_target_entity.rst b/doctrine/resolve_target_entity.rst index 9c5901b354d..c2563f7b4dd 100644 --- a/doctrine/resolve_target_entity.rst +++ b/doctrine/resolve_target_entity.rst @@ -39,9 +39,9 @@ brevity) to explain how to set up and use the ``ResolveTargetEntityListener``. A Customer entity:: - // src/AppBundle/Entity/Customer.php + // src/Entity/Customer.php - namespace AppBundle\Entity; + namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Acme\CustomerBundle\Entity\Customer as BaseCustomer; @@ -112,17 +112,17 @@ about the replacement: .. code-block:: yaml - # app/config/config.yml + # config/packages/doctrine.yaml doctrine: # ... orm: # ... resolve_target_entities: - Acme\InvoiceBundle\Model\InvoiceSubjectInterface: AppBundle\Entity\Customer + Acme\InvoiceBundle\Model\InvoiceSubjectInterface: App\Entity\Customer .. code-block:: xml - + - AppBundle\Entity\Customer + App\Entity\Customer .. code-block:: php - // app/config/config.php + // config/packages/doctrine.php use Acme\InvoiceBundle\Model\InvoiceSubjectInterface; - use AppBundle\Entity\Customer; + use App\Entity\Customer; $container->loadFromExtension('doctrine', array( 'orm' => array( diff --git a/doctrine/reverse_engineering.rst b/doctrine/reverse_engineering.rst index 1e82856bd48..6c4d7b10612 100644 --- a/doctrine/reverse_engineering.rst +++ b/doctrine/reverse_engineering.rst @@ -47,8 +47,7 @@ to a post record thanks to a foreign key constraint. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; Before diving into the recipe, be sure your database connection parameters are -correctly setup in the ``app/config/parameters.yml`` file (or wherever your -database configuration is kept). +correctly setup in the ``.env`` file. The first step towards building entity classes from an existing database is to ask Doctrine to introspect the database and generate the corresponding @@ -57,113 +56,43 @@ table fields. .. code-block:: terminal - $ php bin/console doctrine:mapping:import --force AppBundle xml + $ php bin/console doctrine:mapping:import App\\Entity annotation --path=src/Entity This command line tool asks Doctrine to introspect the database and generate -the XML metadata files under the ``src/AppBundle/Resources/config/doctrine`` -folder of your bundle. This generates two files: ``BlogPost.orm.xml`` and -``BlogComment.orm.xml``. +new PHP classes with annotation metadata into ``src/Entity``. This generates two +files: ``BlogPost.php`` and ``BlogComment.php``. .. tip:: - It's also possible to generate the metadata files in YAML format by changing - the last argument to ``yml``. + It's also possible to generate the metadata files into XML or YAML: -The generated ``BlogPost.orm.xml`` metadata file looks as follows: + .. code-block:: terminal -.. code-block:: xml + $ php bin/console doctrine:mapping:import App\\Entity xml --path=config/doctrine - - - - - - - - - - - +Generating the Getters & Setters or PHP Classes +----------------------------------------------- -Once the metadata files are generated, you can ask Doctrine to build related -entity classes by executing the following command. +The generated PHP classes now have properties and annotation metadata, but they +do *not* have any getter or setter methods. If you generated XML or YAML metadata, +you don't even have the PHP classes! + +To generate the missing getter/setter methods (or to *create* the classes if neceesary), +run: .. code-block:: terminal - // generates entity classes with annotation mappings - $ php bin/console doctrine:mapping:convert annotation ./src - -.. caution:: - - If you want to use annotations, you must remove the XML (or YAML) files - after running this command. This is necessary as - :ref:`it is not possible to mix mapping configuration formats ` - -For example, the newly created ``BlogComment`` entity class looks as follow:: - - // src/AppBundle/Entity/BlogComment.php - namespace AppBundle\Entity; - - use Doctrine\ORM\Mapping as ORM; - - /** - * @ORM\Table(name="blog_comment") - * @ORM\Entity - */ - class BlogComment - { - /** - * @var integer $id - * - * @ORM\Column(name="id", type="bigint") - * @ORM\Id - * @ORM\GeneratedValue(strategy="IDENTITY") - */ - private $id; - - /** - * @var string $author - * - * @ORM\Column(name="author", type="string", length=100, nullable=false) - */ - private $author; - - /** - * @var text $content - * - * @ORM\Column(name="content", type="text", nullable=false) - */ - private $content; - - /** - * @var datetime $createdAt - * - * @ORM\Column(name="created_at", type="datetime", nullable=false) - */ - private $createdAt; - - /** - * @var BlogPost - * - * @ORM\ManyToOne(targetEntity="BlogPost") - * @ORM\JoinColumn(name="post_id", referencedColumnName="id") - */ - private $post; - } - -As you can see, Doctrine converts all table fields to pure private and annotated -class properties. The most impressive thing is that it also discovered the -relationship with the ``BlogPost`` entity class based on the foreign key constraint. -Consequently, you can find a private ``$post`` property mapped with a ``BlogPost`` -entity in the ``BlogComment`` entity class. + // generates getter/setter methods + $ php bin/console make:entity --regenerate App .. note:: - If you want to have a one-to-many relationship, you will need to add - it manually into the entity or to the generated XML or YAML files. - Add a section on the specific entities for one-to-many defining the - ``inversedBy`` and the ``mappedBy`` pieces. + If you want to have a OneToMany relationship, you will need to add + it manually into the entity (e.g. add a ``comments`` property to ``BlogPost``) + or to the generated XML or YAML files. Add a section on the specific entities + for one-to-many defining the ``inversedBy`` and the ``mappedBy`` pieces. The generated entities are now ready to be used. Have fun! .. _`Doctrine tools documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/tools.html#reverse-engineering +.. _`doctrine/doctrine#729`: https://github.com/doctrine/DoctrineBundle/issues/729 diff --git a/email.rst b/email.rst index 4ed365c673c..714d8e0bec2 100644 --- a/email.rst +++ b/email.rst @@ -4,102 +4,52 @@ How to Send an Email ==================== -Sending emails is a classic task for any web application and one that has -special complications and potential pitfalls. Instead of recreating the wheel, -one solution to send emails is to use the SwiftmailerBundle, which leverages -the power of the `Swift Mailer`_ library. This bundle comes with the Symfony -Standard Edition. +Symfony provides a mailer feature based on the popular `Swift Mailer`_ library +via the `SwiftMailerBundle`_. This mailer supports sending messages with your +own mail servers as well as using popular email providers like `Mandrill`_, +`SendGrid`_, and `Amazon SES`_. -.. _swift-mailer-configuration: - -Configuration -------------- - -To use Swift Mailer, you'll need to configure it for your mail server. - -.. tip:: - - Instead of setting up/using your own mail server, you may want to use - a hosted mail provider such as `Mandrill`_, `SendGrid`_, `Amazon SES`_ - or others. These give you an SMTP server, username and password (sometimes - called keys) that can be used with the Swift Mailer configuration. - -In a standard Symfony installation, some ``swiftmailer`` configuration is -already included: +Installation +------------ -.. configuration-block:: +In applications using :doc:`Symfony Flex `, run this command to +install the Swift Mailer based mailer before using it: - .. code-block:: yaml +.. code-block:: terminal - # app/config/config.yml - swiftmailer: - transport: '%mailer_transport%' - host: '%mailer_host%' - username: '%mailer_user%' - password: '%mailer_password%' + $ composer require mailer - .. code-block:: xml +If your application doesn't use Symfony Flex, follow the installation +instructions on `SwiftMailerBundle`_. - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('swiftmailer', array( - 'transport' => "%mailer_transport%", - 'host' => "%mailer_host%", - 'username' => "%mailer_user%", - 'password' => "%mailer_password%", - )); +.. _swift-mailer-configuration: -These values (e.g. ``%mailer_transport%``), are reading from the parameters -that are set in the :ref:`parameters.yml ` file. You -can modify the values in that file, or set the values directly here. +Configuration +------------- -The following configuration attributes are available: +The ``config/packages/swiftmailer.yaml`` file that's created when installing the +mailer provides all the initial config needed to send emails, except your mail +server connection details. Those parameters are defined in the ``MAILER_URL`` +environment variable in the ``.env`` file: -* ``transport`` (``smtp``, ``mail``, ``sendmail``, or ``gmail``) -* ``username`` -* ``password`` -* ``host`` -* ``port`` -* ``encryption`` (``tls``, or ``ssl``) -* ``auth_mode`` (``plain``, ``login``, or ``cram-md5``) -* ``spool`` +.. code-block:: bash - * ``type`` (how to queue the messages, ``file`` or ``memory`` is supported, see :doc:`/email/spool`) - * ``path`` (where to store the messages) -* ``delivery_addresses`` (an array of email addresses where to send ALL emails) -* ``disable_delivery`` (set to true to disable delivery completely) + # use this to disable email delivery + MAILER_URL=null://localhost -.. caution:: + # use this to configure a traditional SMTP server + MAILER_URL=smtp://localhost:25?encryption=ssl&auth_mode=login&username=&password= - Starting from SwiftMailer 5.4.5, the ``mail`` transport is deprecated - and will be removed in version 6. Consider using another transport like - ``smtp``, ``sendmail`` or ``gmail``. +Refer to the :doc:`SwiftMailer configuration reference ` +for the detailed explanation of all the available config options. Sending Emails -------------- The Swift Mailer library works by creating, configuring and then sending ``Swift_Message`` objects. The "mailer" is responsible for the actual delivery -of the message and is accessible via the ``mailer`` service. Overall, sending -an email is pretty straightforward:: +of the message and is accessible via the ``Swift_Mailer`` service. Overall, +sending an email is pretty straightforward:: public function indexAction($name, \Swift_Mailer $mailer) { @@ -108,8 +58,8 @@ an email is pretty straightforward:: ->setTo('recipient@example.com') ->setBody( $this->renderView( - // app/Resources/views/Emails/registration.html.twig - 'Emails/registration.html.twig', + // templates/emails/registration.html.twig + 'emails/registration.html.twig', array('name' => $name) ), 'text/html' @@ -118,7 +68,7 @@ an email is pretty straightforward:: * If you also want to include a plaintext version of the message ->addPart( $this->renderView( - 'Emails/registration.txt.twig', + 'emails/registration.txt.twig', array('name' => $name) ), 'text/plain' @@ -128,9 +78,6 @@ an email is pretty straightforward:: $mailer->send($message); - // or, you can also fetch the mailer service this way - // $this->get('mailer')->send($message); - return $this->render(...); } @@ -140,7 +87,7 @@ template might look something like this: .. code-block:: html+jinja - {# app/Resources/views/Emails/registration.html.twig #} + {# templates/emails/registration.html.twig #}

You did it! You registered!

Hi {{ name }}! You're successfully registered. @@ -154,20 +101,69 @@ template might look something like this: The ``$message`` object supports many more options, such as including attachments, -adding HTML content, and much more. Fortunately, Swift Mailer covers the topic -of `Creating Messages`_ in great detail in its documentation. +adding HTML content, and much more. Refer to the `Creating Messages`_ section +of the Swift Mailer documentation for more details. + +.. _email-using-gmail: + +Using Gmail to Send Emails +-------------------------- + +During development, you might prefer to send emails using Gmail instead of +setting up a regular SMTP server. To do that, update the ``MAILER_URL`` of your +``.env`` file to this: + +.. code-block:: bash + + # username is your full Gmail or Google Apps email address + MAILER_URL=gmail://username:password@localhost + +The ``gmail`` transport is simply a shortcut that uses the ``smtp`` transport, +``ssl`` encryption, ``login`` auth mode and ``smtp.gmail.com`` host. If your app +uses other encryption or auth mode, you must override those values +(:doc:`see mailer config reference `): + +.. code-block:: bash + + # username is your full Gmail or Google Apps email address + MAILER_URL=gmail://username:password@localhost?encryption=tls&auth_mode=oauth + +If your Gmail account uses 2-Step-Verification, you must `generate an App password`_ +and use it as the value of the mailer password. You must also ensure that you +`allow less secure apps to access your Gmail account`_. + +Using Cloud Services to Send Emails +----------------------------------- + +Cloud mailing services are a popular option for companies that don't want to set +up and maintain their own reliable mail servers. In Symfony apps, using these +services is as simple as updating the value of ``MAILER_URL`` in the ``.env`` +file. For example, for `Amazon SES`_ (Simple Email Service): + +.. code-block:: bash + + # The host will be different depending on your AWS zone + # The username/password credentials are obtained from the Amazon SES console + MAILER_URL=smtp://email-smtp.us-east-1.amazonaws.com:587?encryption=tls&username=YOUR_SES_USERNAME&password=YOUR_SES_PASSWORD + +Use the same technique for other mail services, as most of the time there is +nothing more to it than configuring an SMTP endpoint. Learn more ---------- .. toctree:: :maxdepth: 1 - :glob: - email/* + email/dev_environment + email/spool + email/testing .. _`Swift Mailer`: http://swiftmailer.org/ +.. _`SwiftMailerBundle`: https://github.com/symfony/swiftmailer-bundle .. _`Creating Messages`: http://swiftmailer.org/docs/messages.html .. _`Mandrill`: https://mandrill.com/ .. _`SendGrid`: https://sendgrid.com/ .. _`Amazon SES`: http://aws.amazon.com/ses/ +.. _`generate an App password`: https://support.google.com/accounts/answer/185833 +.. _`allow less secure apps to access your Gmail account`: https://support.google.com/accounts/answer/6010255 diff --git a/email/cloud.rst b/email/cloud.rst deleted file mode 100644 index d7ddbd33f3b..00000000000 --- a/email/cloud.rst +++ /dev/null @@ -1,120 +0,0 @@ -.. index:: - single: Emails; Using the cloud - -How to Use the Cloud to Send Emails -=================================== - -Requirements for sending emails from a production system differ from your -development setup as you don't want to be limited in the number of emails, -the sending rate or the sender address. Thus, -:doc:`using Gmail ` or similar services is not an -option. If setting up and maintaining your own reliable mail server causes -you a headache there's a simple solution: Leverage the cloud to send your -emails. - -This article shows how easy it is to integrate -`Amazon's Simple Email Service (SES)`_ into Symfony. - -.. note:: - - You can use the same technique for other mail services, as most of the - time there is nothing more to it than configuring an SMTP endpoint for - Swift Mailer. - -In the Symfony configuration, change the Swift Mailer settings ``transport``, -``host``, ``port`` and ``encryption`` according to the information provided in -the `SES console`_. Create your individual SMTP credentials in the SES console -and complete the configuration with the provided ``username`` and ``password``: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - swiftmailer: - transport: smtp - host: email-smtp.us-east-1.amazonaws.com - port: 587 # different ports are available, see SES console - encryption: tls # TLS encryption is required - username: AWS_SES_SMTP_USERNAME # to be created in the SES console - password: AWS_SES_SMTP_PASSWORD # to be created in the SES console - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('swiftmailer', array( - 'transport' => 'smtp', - 'host' => 'email-smtp.us-east-1.amazonaws.com', - 'port' => 587, - 'encryption' => 'tls', - 'username' => 'AWS_SES_SMTP_USERNAME', - 'password' => 'AWS_SES_SMTP_PASSWORD', - )); - -The ``port`` and ``encryption`` keys are not present in the Symfony Standard -Edition configuration by default, but you can simply add them as needed. - -And that's it, you're ready to start sending emails through the cloud! - -.. tip:: - - If you are using the Symfony Standard Edition, configure the parameters in - ``parameters.yml`` and use them in your configuration files. This allows - for different Swift Mailer configurations for each installation of your - application. For instance, use Gmail during development and the cloud in - production. - - .. code-block:: yaml - - # app/config/parameters.yml - parameters: - # ... - mailer_transport: smtp - mailer_host: email-smtp.us-east-1.amazonaws.com - mailer_port: 587 # different ports are available, see SES console - mailer_encryption: tls # TLS encryption is required - mailer_user: AWS_SES_SMTP_USERNAME # to be created in the SES console - mailer_password: AWS_SES_SMTP_PASSWORD # to be created in the SES console - -.. note:: - - If you intend to use Amazon SES, please note the following: - - * You have to sign up to `Amazon Web Services (AWS)`_; - - * Every sender address used in the ``From`` or ``Return-Path`` (bounce - address) header needs to be confirmed by the owner. You can also - confirm an entire domain; - - * Initially you are in a restricted sandbox mode. You need to request - production access before being allowed to send to arbitrary - recipients; - - * SES may be subject to a charge. - -.. _`Amazon's Simple Email Service (SES)`: http://aws.amazon.com/ses -.. _`SES console`: https://console.aws.amazon.com/ses -.. _`Amazon Web Services (AWS)`: http://aws.amazon.com diff --git a/email/dev_environment.rst b/email/dev_environment.rst index 6234f96e52d..9938b976b84 100644 --- a/email/dev_environment.rst +++ b/email/dev_environment.rst @@ -6,7 +6,7 @@ How to Work with Emails during Development When developing an application which sends email, you will often not want to actually send the email to the specified recipient during -development. If you are using the SwiftmailerBundle with Symfony, you +development. If you are using the default Symfony mailer, you can easily achieve this through configuration settings without having to make any changes to your application's code at all. There are two main choices when it comes to handling email during development: (a) disabling the @@ -16,23 +16,21 @@ address (with optional exceptions). Disabling Sending ----------------- -You can disable sending email by setting the ``disable_delivery`` option -to ``true``. This is the default in the ``test`` environment in the Standard -distribution. If you do this in the ``test`` specific config then email -will not be sent when you run tests, but will continue to be sent in the -``prod`` and ``dev`` environments: +You can disable sending email by setting the ``disable_delivery`` option to +``true``, which is the default value used by Symfony in the ``test`` environment +(email messages will continue to be sent in the other environments): .. configuration-block:: .. code-block:: yaml - # app/config/config_test.yml + # config/packages/test/swiftmailer.yaml swiftmailer: disable_delivery: true .. code-block:: xml - + loadFromExtension('swiftmailer', array( 'disable_delivery' => "true", )); -If you'd also like to disable deliver in the ``dev`` environment, simply -add this same configuration to the ``config_dev.yml`` file. - .. _sending-to-a-specified-address: Sending to a Specified Address(es) @@ -67,13 +62,13 @@ via the ``delivery_addresses`` option: .. code-block:: yaml - # app/config/config_dev.yml + # config/packages/dev/swiftmailer.yaml swiftmailer: delivery_addresses: ['dev@example.com'] .. code-block:: xml - + loadFromExtension('swiftmailer', array( 'delivery_addresses' => array("dev@example.com"), )); -Now, suppose you're sending an email to ``recipient@example.com``:: +Now, suppose you're sending an email to ``recipient@example.com`` in a controller:: - public function indexAction($name, \Swift_Mailer $mailer) + public function index($name, \Swift_Mailer $mailer) { $message = (new \Swift_Message('Hello Email')) ->setFrom('send@example.com') @@ -141,7 +136,7 @@ by adding the ``delivery_whitelist`` option: .. code-block:: yaml - # app/config/config_dev.yml + # config/packages/dev/swiftmailer.yaml swiftmailer: delivery_addresses: ['dev@example.com'] delivery_whitelist: @@ -152,7 +147,7 @@ by adding the ``delivery_whitelist`` option: .. code-block:: xml - + loadFromExtension('swiftmailer', array( 'delivery_addresses' => array("dev@example.com"), 'delivery_whitelist' => array( @@ -205,20 +200,20 @@ the web debug toolbar will not display an email icon or a report on the next page. Instead, you can set the ``intercept_redirects`` option to ``true`` in the -``config_dev.yml`` file, which will cause the redirect to stop and allow -you to open the report with details of the sent emails. +``dev`` environment, which will cause the redirect to stop and allow you to open +the report with details of the sent emails. .. configuration-block:: .. code-block:: yaml - # app/config/config_dev.yml + # config/packages/dev/web_profiler.yaml web_profiler: intercept_redirects: true .. code-block:: xml - + loadFromExtension('web_profiler', array( 'intercept_redirects' => 'true', )); diff --git a/email/gmail.rst b/email/gmail.rst deleted file mode 100644 index e34604bae0a..00000000000 --- a/email/gmail.rst +++ /dev/null @@ -1,133 +0,0 @@ -.. index:: - single: Emails; Gmail - -How to Use Gmail to Send Emails -=============================== - -During development, instead of using a regular SMTP server to send emails, you -might find using Gmail easier and more practical. The SwiftmailerBundle makes -it really easy. - -In the development configuration file, change the ``transport`` setting to -``gmail`` and set the ``username`` and ``password`` to the Google credentials: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config_dev.yml - swiftmailer: - transport: gmail - username: your_gmail_username - password: your_gmail_password - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // app/config/config_dev.php - $container->loadFromExtension('swiftmailer', array( - 'transport' => 'gmail', - 'username' => 'your_gmail_username', - 'password' => 'your_gmail_password', - )); - -.. tip:: - - It's more convenient to configure these options in the ``parameters.yml`` - file: - - .. code-block:: yaml - - # app/config/parameters.yml - parameters: - # ... - mailer_user: your_gmail_username - mailer_password: your_gmail_password - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/config_dev.yml - swiftmailer: - transport: gmail - username: '%mailer_user%' - password: '%mailer_password%' - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // app/config/config_dev.php - $container->loadFromExtension('swiftmailer', array( - 'transport' => 'gmail', - 'username' => '%mailer_user%', - 'password' => '%mailer_password%', - )); - -Redefining the Default Configuration Parameters ------------------------------------------------ - -The ``gmail`` transport is simply a shortcut that uses the ``smtp`` transport -and sets these options: - -============== ================== -Option Value -============== ================== -``encryption`` ``ssl`` -``auth_mode`` ``login`` -``host`` ``smtp.gmail.com`` -============== ================== - -If your application uses ``tls`` encryption or ``oauth`` authentication, you -must override the default options by defining the ``encryption`` and ``auth_mode`` -parameters. - -If your Gmail account uses 2-Step-Verification, you must `generate an App password`_ -and use it as the value of the ``mailer_password`` parameter. You must also ensure -that you `allow less secure apps to access your Gmail account`_. - -.. seealso:: - - See the :doc:`Swiftmailer configuration reference ` - for more details. - -.. _`generate an App password`: https://support.google.com/accounts/answer/185833 -.. _`allow less secure apps to access your Gmail account`: https://support.google.com/accounts/answer/6010255 diff --git a/email/spool.rst b/email/spool.rst index b4115c86767..e3fc88fe8e4 100644 --- a/email/spool.rst +++ b/email/spool.rst @@ -4,16 +4,18 @@ How to Spool Emails =================== -When you are using the SwiftmailerBundle to send an email from a Symfony -application, it will default to sending the email immediately. You may, however, -want to avoid the performance hit of the communication between Swift Mailer -and the email transport, which could cause the user to wait for the next -page to load while the email is sending. This can be avoided by choosing -to "spool" the emails instead of sending them directly. This means that Swift Mailer -does not attempt to send the email but instead saves the message to somewhere -such as a file. Another process can then read from the spool and take care -of sending the emails in the spool. Currently only spooling to file or memory is supported -by Swift Mailer. +The default behavior of the Symfony mailer is to send the email messages +immediately. You may, however, want to avoid the performance hit of the +communication to the email server, which could cause the user to wait for the +next page to load while the email is sending. This can be avoided by choosing to +"spool" the emails instead of sending them directly. + +This makes the mailer to not attempt to send the email message but instead save +it somewhere such as a file. Another process can then read from the spool and +take care of sending the emails in the spool. Currently only spooling to file or +memory is supported. + +.. _email-spool-memory: Spool Using Memory ------------------ @@ -21,20 +23,20 @@ Spool Using Memory When you use spooling to store the emails to memory, they will get sent right before the kernel terminates. This means the email only gets sent if the whole request got executed without any unhandled exception or any errors. To configure -swiftmailer with the memory option, use the following configuration: +this spool, use the following configuration: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/packages/swiftmailer.yaml swiftmailer: # ... spool: { type: memory } .. code-block:: xml - + loadFromExtension('swiftmailer', array( // ... 'spool' => array('type' => 'memory'), @@ -71,7 +73,7 @@ In order to use the spool with files, use the following configuration: .. code-block:: yaml - # app/config/config.yml + # config/packages/swiftmailer.yaml swiftmailer: # ... spool: @@ -80,7 +82,7 @@ In order to use the spool with files, use the following configuration: .. code-block:: xml - + loadFromExtension('swiftmailer', array( // ... @@ -117,7 +119,7 @@ In order to use the spool with files, use the following configuration: .. code-block:: yaml - path: '%kernel.project_dir%/app/spool' + path: '%kernel.project_dir%/var/spool' Now, when your app sends an email, it will not actually be sent but instead added to the spool. Sending the messages from the spool is done separately. diff --git a/email/testing.rst b/email/testing.rst index 0e21ff31344..1da89c7315b 100644 --- a/email/testing.rst +++ b/email/testing.rst @@ -10,9 +10,9 @@ SwiftmailerBundle, which leverages the power of the `Swift Mailer`_ library. To functionally test that an email was sent, and even assert the email subject, content or any other headers, you can use :doc:`the Symfony Profiler `. -Start with an easy controller action that sends an email:: +Start with a simple controller action that sends an email:: - public function sendEmailAction($name, \Swift_Mailer $mailer) + public function sendEmail($name, \Swift_Mailer $mailer) { $message = (new \Swift_Message('Hello Email')) ->setFrom('send@example.com') @@ -28,8 +28,8 @@ Start with an easy controller action that sends an email:: In your functional test, use the ``swiftmailer`` collector on the profiler to get information about the messages sent on the previous request:: - // tests/AppBundle/Controller/MailControllerTest.php - namespace Tests\AppBundle\Controller; + // tests/Controller/MailControllerTest.php + namespace App\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; @@ -73,8 +73,8 @@ Problem: The Collector Object Is ``null`` The email collector is only available when the profiler is enabled and collects information, as explained in :doc:`/testing/profiling`. -Problem: The Collector Doesn't Contain the E-Mail -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Problem: The Collector Doesn't Contain the Email +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a redirection is performed after sending the email (for example when you send an email after a form is processed and before redirecting to another page), make diff --git a/event_dispatcher.rst b/event_dispatcher.rst index 5dc63d28cce..9205ca03919 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -23,8 +23,8 @@ Creating an Event Listener The most common way to listen to an event is to register an **event listener**:: - // src/AppBundle/EventListener/ExceptionListener.php - namespace AppBundle\EventListener; + // src/EventListener/ExceptionListener.php + namespace App\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Response; @@ -75,15 +75,15 @@ using a special "tag": .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\EventListener\ExceptionListener: + App\EventListener\ExceptionListener: tags: - { name: kernel.event_listener, event: kernel.exception } .. code-block:: xml - + - + @@ -99,27 +99,39 @@ using a special "tag": .. code-block:: php - // app/config/services.php - use AppBundle\EventListener\ExceptionListener; + // config/services.php + use App\EventListener\ExceptionListener; $container ->autowire(ExceptionListener::class) ->addTag('kernel.event_listener', array('event' => 'kernel.exception')) ; -.. note:: +Symfony follows this logic to decide which method to execute inside the event +listener class: + +#. If the ``kernel.event_listener`` tag defines the ``method`` attribute, that's + the name of the method to be executed; +#. If no ``method`` attribute is defined, try to execute the method whose name + is ``on`` + "camel-cased event name" (e.g. ``onKernelException()`` method for + the ``kernel.exception`` event); +#. If that method is not defined either, try to execute the ``__invoke()`` magic + method (which makes event listeners invokable); +#. If the ``_invoke()`` method is not defined either, throw an exception. - There is an optional tag attribute called ``method`` which defines which method - to execute when the event is triggered. By default the name of the method is - ``on`` + "camel-cased event name". If the event is ``kernel.exception`` the - method executed by default is ``onKernelException()``. +.. versionadded:: 4.1 + The support of the ``__invoke()`` method to create invokable event listeners + was introduced in Symfony 4.1. + +.. note:: - The other optional tag attribute is called ``priority``, which defaults to - ``0`` and it controls the order in which listeners are executed (the highest - the priority, the earlier a listener is executed). This is useful when you - need to guarantee that one listener is executed before another. The priorities - of the internal Symfony listeners usually range from ``-255`` to ``255`` but - your own listeners can use any positive or negative integer. + There is an optional attribute for the ``kernel.event_listener`` tag called + ``priority``, which defaults to ``0`` and it controls the order in which + listeners are executed (the highest the priority, the earlier a listener is + executed). This is useful when you need to guarantee that one listener is + executed before another. The priorities of the internal Symfony listeners + usually range from ``-255`` to ``255`` but your own listeners can use any + positive or negative integer. .. _events-subscriber: @@ -139,8 +151,8 @@ about event subscribers, read :doc:`/components/event_dispatcher`. The following example shows an event subscriber that defines several methods which listen to the same ``kernel.exception`` event:: - // src/AppBundle/EventSubscriber/ExceptionSubscriber.php - namespace AppBundle\EventSubscriber; + // src/EventSubscriber/ExceptionSubscriber.php + namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -176,7 +188,7 @@ listen to the same ``kernel.exception`` event:: } } -That's it! Your ``services.yml`` file should already be setup to load services from +That's it! Your ``services.yaml`` file should already be setup to load services from the ``EventSubscriber`` directory. Symfony takes care of the rest. .. _ref-event-subscriber-configuration: @@ -196,8 +208,8 @@ sub-requests - typically by :doc:`/templating/embedding_controllers`). For the c Symfony events, you might need to check to see if the event is for a "master" request or a "sub request":: - // src/AppBundle/EventListener/RequestListener.php - namespace AppBundle\EventListener; + // src/EventListener/RequestListener.php + namespace App\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\HttpKernel; @@ -256,6 +268,6 @@ Learn more .. toctree:: :maxdepth: 1 - :glob: - event_dispatcher/* + event_dispatcher/before_after_filters + event_dispatcher/method_behavior diff --git a/event_dispatcher/before_after_filters.rst b/event_dispatcher/before_after_filters.rst index 6e38cfe4846..066b495e79c 100644 --- a/event_dispatcher/before_after_filters.rst +++ b/event_dispatcher/before_after_filters.rst @@ -33,14 +33,13 @@ token. Before Filters with the ``kernel.controller`` Event --------------------------------------------------- -First, store some basic token configuration using ``config.yml`` and the -parameters key: +First, define some token configuration as parameters: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/services.yaml parameters: tokens: client1: pass1 @@ -48,7 +47,7 @@ parameters key: .. code-block:: xml - + setParameter('tokens', array( 'client1' => 'pass1', 'client2' => 'pass2', @@ -81,7 +80,7 @@ some way to identify if the controller that matches the request needs token vali A clean and easy way is to create an empty interface and make the controllers implement it:: - namespace AppBundle\Controller; + namespace App\Controller; interface TokenAuthenticatedController { @@ -90,15 +89,15 @@ implement it:: A controller that implements this interface simply looks like this:: - namespace AppBundle\Controller; + namespace App\Controller; - use AppBundle\Controller\TokenAuthenticatedController; + use App\Controller\TokenAuthenticatedController; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class FooController extends Controller implements TokenAuthenticatedController { // An action that needs authentication - public function barAction() + public function bar() { // ... } @@ -111,10 +110,10 @@ Next, you'll need to create an event listener, which will hold the logic that you want to be executed before your controllers. If you're not familiar with event listeners, you can learn more about them at :doc:`/event_dispatcher`:: - // src/AppBundle/EventSubscriber/TokenSubscriber.php - namespace AppBundle\EventSubscriber; + // src/EventSubscriber/TokenSubscriber.php + namespace App\EventSubscriber; - use AppBundle\Controller\TokenAuthenticatedController; + use App\Controller\TokenAuthenticatedController; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -158,7 +157,7 @@ event listeners, you can learn more about them at :doc:`/event_dispatcher`:: } } -That's it! Your ``services.yml`` file should already be setup to load services from +That's it! Your ``services.yaml`` file should already be setup to load services from the ``EventSubscriber`` directory. Symfony takes care of the rest. Your ``TokenSubscriber`` ``onKernelController()`` method will be executed on each request. If the controller that is about to be executed implements ``TokenAuthenticatedController``, diff --git a/event_dispatcher/method_behavior.rst b/event_dispatcher/method_behavior.rst index ffd58b25d9c..b99dbb748d2 100644 --- a/event_dispatcher/method_behavior.rst +++ b/event_dispatcher/method_behavior.rst @@ -41,8 +41,8 @@ executed, and ``mailer.post_send`` after the method is executed. Each uses a custom Event class to communicate information to the listeners of the two events. For example, ``BeforeSendMailEvent`` might look like this:: - // src/AppBundle/Event/BeforeSendMailEvent.php - namespace AppBundle\Event; + // src/Event/BeforeSendMailEvent.php + namespace App\Event; use Symfony\Component\EventDispatcher\Event; @@ -80,8 +80,8 @@ events. For example, ``BeforeSendMailEvent`` might look like this:: And the ``AfterSendMailEvent`` even like this:: - // src/AppBundle/Event/AfterSendMailEvent.php - namespace AppBundle\Event; + // src/Event/AfterSendMailEvent.php + namespace App\Event; use Symfony\Component\EventDispatcher\Event; @@ -111,11 +111,11 @@ that information (e.g. ``setMessage()``). Now, you can create an event subscriber to hook into this event. For example, you could listen to the ``mailer.post_send`` event and change the method's return value:: - // src/AppBundle/EventSubscriber/MailPostSendSubscriber.php - namespace AppBundle\EventSubscriber; + // src/EventSubscriber/MailPostSendSubscriber.php + namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use AppBundle\Event\AfterSendMailEvent; + use App\Event\AfterSendMailEvent; class MailPostSendSubscriber implements EventSubscriberInterface { diff --git a/form/action_method.rst b/form/action_method.rst index 5178a917887..b044420f28a 100644 --- a/form/action_method.rst +++ b/form/action_method.rst @@ -15,8 +15,8 @@ form, you can use ``setAction()`` and ``setMethod()``: .. code-block:: php-symfony - // AppBundle/Controller/DefaultController.php - namespace AppBundle\Controller; + // src/Controller/DefaultController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Form\Extension\Core\Type\DateType; @@ -25,7 +25,7 @@ form, you can use ``setAction()`` and ``setMethod()``: class DefaultController extends Controller { - public function newAction() + public function new() { $form = $this->createFormBuilder($task) ->setAction($this->generateUrl('target_route')) @@ -75,15 +75,15 @@ options: .. code-block:: php-symfony - // AppBundle/Controller/DefaultController.php - namespace AppBundle\Controller; + // src/Controller/DefaultController.php + namespace App\Controller; + use App\Form\TaskType; use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use AppBundle\Form\TaskType; class DefaultController extends Controller { - public function newAction() + public function new() { // ... @@ -98,8 +98,8 @@ options: .. code-block:: php-standalone + use App\Form\TaskType; use Symfony\Component\Form\Forms; - use AppBundle\Form\TaskType; $formFactoryBuilder = Forms::createFormFactoryBuilder(); @@ -119,15 +119,13 @@ to the ``form()`` or the ``form_start()`` helper functions: .. code-block:: html+twig - {# app/Resources/views/default/new.html.twig #} + {# templates/default/new.html.twig #} {{ form_start(form, {'action': path('target_route'), 'method': 'GET'}) }} .. code-block:: html+php - + start($form, array( - // The path() method was introduced in Symfony 2.8. Prior to 2.8, - // you had to use generate(). 'action' => $view['router']->path('target_route'), 'method' => 'GET', )) ?> diff --git a/form/create_custom_field_type.rst b/form/create_custom_field_type.rst index c62928c3632..5aa5d822a8c 100644 --- a/form/create_custom_field_type.rst +++ b/form/create_custom_field_type.rst @@ -16,11 +16,11 @@ Defining the Field Type In order to create the custom field type, first you have to create the class representing the field. In this situation the class holding the field type will be called ``ShippingType`` and the file will be stored in the default location -for form fields, which is ``\Form\Type``. Make sure the field extends +for form fields, which is ``App\Form\Type``. Make sure the field extends :class:`Symfony\\Component\\Form\\AbstractType`:: - // src/AppBundle/Form/Type/ShippingType.php - namespace AppBundle\Form\Type; + // src/Form/Type/ShippingType.php + namespace App\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -36,7 +36,6 @@ for form fields, which is ``\Form\Type``. Make sure the field extend 'Expedited Shipping' => 'expedited', 'Priority Shipping' => 'priority', ), - 'choices_as_values' => true, )); } @@ -104,7 +103,7 @@ the class name of your type. For more information, see :ref:`form-customization- When the name of your form class matches any of the built-in field types, your form might not be rendered correctly. A form type named - ``AppBundle\Form\PasswordType`` will have the same block name as the + ``App\Form\PasswordType`` will have the same block name as the built-in ``PasswordType`` and won't be rendered correctly. Override the ``getBlockPrefix()`` method to return a unique block prefix (e.g. ``app_password``) to avoid collisions. @@ -120,7 +119,7 @@ link for details), create a ``shipping_widget`` block to handle this: .. code-block:: html+twig - {# app/Resources/views/form/fields.html.twig #} + {# templates/form/fields.html.twig #} {% block shipping_widget %} {% spaceless %} {% if expanded %} @@ -141,7 +140,7 @@ link for details), create a ``shipping_widget`` block to handle this: .. code-block:: html+php - +
    block($form, 'widget_container_attributes') ?>> @@ -177,14 +176,14 @@ link for details), create a ``shipping_widget`` block to handle this: .. code-block:: yaml - # app/config/config.yml + # config/packages/twig.yaml twig: form_themes: - 'form/fields.html.twig' .. code-block:: xml - + loadFromExtension('twig', array( 'form_themes' => array( 'form/fields.html.twig', @@ -214,7 +213,7 @@ link for details), create a ``shipping_widget`` block to handle this: .. code-block:: yaml - # app/config/config.yml + # config/packages/framework.yaml framework: templating: form: @@ -223,7 +222,7 @@ link for details), create a ``shipping_widget`` block to handle this: .. code-block:: xml - + loadFromExtension('framework', array( 'templating' => array( 'form' => array( @@ -259,12 +258,12 @@ Using the Field Type You can now use your custom field type immediately, simply by creating a new instance of the type in one of your forms:: - // src/AppBundle/Form/Type/OrderType.php - namespace AppBundle\Form\Type; + // src/Form/Type/OrderType.php + namespace App\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; - use AppBundle\Form\Type\ShippingType; + use App\Form\Type\ShippingType; class OrderType extends AbstractType { @@ -289,8 +288,8 @@ Accessing Services and Config If you need to access :doc:`services ` from your form class, add a ``__construct()`` method like normal:: - // src/AppBundle/Form/Type/ShippingType.php - namespace AppBundle\Form\Type; + // src/Form/Type/ShippingType.php + namespace App\Form\Type; // ... use Doctrine\ORM\EntityManagerInterface; @@ -307,7 +306,7 @@ add a ``__construct()`` method like normal:: // use $this->entityManager down anywhere you want ... } -If you're using the default ``services.yml`` configuration (i.e. services from the +If you're using the default ``services.yaml`` configuration (i.e. services from the ``Form/`` are loaded and ``autoconfigure`` is enabled), this will already work! See :ref:`service-container-creating-service` for more details. diff --git a/form/create_form_type_extension.rst b/form/create_form_type_extension.rst index 38e7676cf90..568c3d1275f 100644 --- a/form/create_form_type_extension.rst +++ b/form/create_form_type_extension.rst @@ -24,8 +24,8 @@ Defining the Form Type Extension First, create the form type extension class:: - // src/AppBundle/Form/Extension/ImageTypeExtension.php - namespace AppBundle\Form\Extension; + // src/Form/Extension/ImageTypeExtension.php + namespace App\Form\Extension; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\Extension\Core\Type\FileType; @@ -71,15 +71,17 @@ your class as a service and using the ``form.type_extension`` tag: .. code-block:: yaml + # config/services.yaml services: # ... - AppBundle\Form\Extension\ImageTypeExtension: + App\Form\Extension\ImageTypeExtension: tags: - { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FileType } .. code-block:: xml + - + @@ -95,7 +97,8 @@ your class as a service and using the ``form.type_extension`` tag: .. code-block:: php - use AppBundle\Form\Extension\ImageTypeExtension; + // config/services.php + use App\Form\Extension\ImageTypeExtension; use Symfony\Component\Form\Extension\Core\Type\FileType; $container->autowire(ImageTypeExtension::class) @@ -109,10 +112,6 @@ the ``getExtendedType()`` method. As *soon* as you do this, any method that you' overridden (e.g. ``buildForm()``) will be called whenever *any* field of the given type (``FileType``) is built. Let's see an example next. -.. versionadded:: 3.3 - Prior to Symfony 3.3, you needed to define type extension services as ``public``. - Starting from Symfony 3.3, you can also define them as ``private``. - .. tip:: There is an optional tag attribute called ``priority``, which @@ -121,9 +120,6 @@ type (``FileType``) is built. Let's see an example next. an extension is loaded). This is useful when you need to guarantee that one extension is loaded before or after another extension. - .. versionadded:: 3.2 - The ``priority`` attribute was introduced in Symfony 3.2. - Adding the extension Business Logic ----------------------------------- @@ -134,8 +130,8 @@ you use an approach similar to the one described in you have a Media model with a path property, corresponding to the image path in the database:: - // src/AppBundle/Entity/Media.php - namespace AppBundle\Entity; + // src/Entity/Media.php + namespace App\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -167,8 +163,8 @@ the ``FileType::class`` form type: For example:: - // src/AppBundle/Form/Extension/ImageTypeExtension.php - namespace AppBundle\Form\Extension; + // src/Form/Extension/ImageTypeExtension.php + namespace App\Form\Extension; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormView; @@ -224,7 +220,7 @@ Specifically, you need to override the ``file_widget`` block: .. code-block:: html+twig - {# app/Resources/fields.html.twig #} + {# templates/form/fields.html.twig #} {% extends 'form_div_layout.html.twig' %} {% block file_widget %} @@ -240,7 +236,7 @@ Specifically, you need to override the ``file_widget`` block: .. code-block:: html+php - + widget($form) ?> @@ -256,8 +252,8 @@ From now on, when adding a field of type ``FileType::class`` to your form, you c specify an ``image_property`` option that will be used to display an image next to the file field. For example:: - // src/AppBundle/Form/Type/MediaType.php - namespace AppBundle\Form\Type; + // src/Form/Type/MediaType.php + namespace App\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; diff --git a/form/csrf_protection.rst b/form/csrf_protection.rst deleted file mode 100644 index 266555579c0..00000000000 --- a/form/csrf_protection.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. index:: - single: Forms; CSRF protection - -How to Implement CSRF Protection -================================ - -CSRF - or `Cross-site request forgery`_ - is a method by which a malicious -user attempts to make your legitimate users unknowingly submit data that -they don't intend to submit. Fortunately, CSRF attacks can be prevented by -using a CSRF token inside your forms. - -The good news is that, by default, Symfony embeds and validates CSRF tokens -automatically for you. This means that you can take advantage of the CSRF -protection without doing anything. In fact, every form in this article has -taken advantage of the CSRF protection! - -CSRF protection works by adding a hidden field to your form - called ``_token`` -by default - that contains a value that only you and your user knows. This -ensures that the user - not some other entity - is submitting the given data. -Symfony automatically validates the presence and accuracy of this token. - -The ``_token`` field is a hidden field and will be automatically rendered -if you include the ``form_end()`` function in your template, which ensures -that all un-rendered fields are output. - -.. caution:: - - Since the token is stored in the session, a session is started automatically - as soon as you render a form with CSRF protection. - -The CSRF token can be customized on a form-by-form basis. For example:: - - // ... - use AppBundle\Entity\Task; - use Symfony\Component\OptionsResolver\OptionsResolver; - - class TaskType extends AbstractType - { - // ... - - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults(array( - 'data_class' => Task::class, - 'csrf_protection' => true, - 'csrf_field_name' => '_token', - // a unique key to help generate the secret token - 'csrf_token_id' => 'task_item', - )); - } - - // ... - } - -.. _form-disable-csrf: - -To disable CSRF protection, set the ``csrf_protection`` option to false. -Customizations can also be made globally in your project. For more information, -see the :ref:`form configuration reference ` -section. - -.. note:: - - The ``csrf_token_id`` option is optional but greatly enhances the security - of the generated token by making it different for each form. - -.. caution:: - - CSRF tokens are meant to be different for every user. This is why you - need to be cautious if you try to cache pages with forms including this - kind of protection. For more information, see - :doc:`/http_cache/form_csrf_caching`. - -.. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery diff --git a/form/data_based_validation.rst b/form/data_based_validation.rst index 3e31be7cd67..c8a74bb99f3 100644 --- a/form/data_based_validation.rst +++ b/form/data_based_validation.rst @@ -8,7 +8,7 @@ If you need some advanced logic to determine the validation groups (e.g. based on submitted data), you can set the ``validation_groups`` option to an array callback:: - use AppBundle\Entity\Client; + use App\Entity\Client; use Symfony\Component\OptionsResolver\OptionsResolver; // ... @@ -27,7 +27,7 @@ This will call the static method ``determineValidationGroups()`` on the The Form object is passed as an argument to that method (see next example). You can also define whole logic inline by using a ``Closure``:: - use AppBundle\Entity\Client; + use App\Entity\Client; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -51,7 +51,7 @@ Using the ``validation_groups`` option overrides the default validation group which is being used. If you want to validate the default constraints of the entity as well you have to adjust the option as follows:: - use AppBundle\Entity\Client; + use App\Entity\Client; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; diff --git a/form/data_transformers.rst b/form/data_transformers.rst index 3f66c1f1406..acdad534f7c 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -23,10 +23,10 @@ Simple Example: Transforming String Tags from User Input to an Array Suppose you have a Task form with a tags ``text`` type:: - // src/AppBundle/Form/Type/TaskType.php - namespace AppBundle\Form\Type; + // src/Form/Type/TaskType.php + namespace App\Form\Type; - use AppBundle\Entity\Task; + use App\Entity\Task; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -56,8 +56,8 @@ This is a *perfect* time to attach a custom data transformer to the ``tags`` field. The easiest way to do this is with the :class:`Symfony\\Component\\Form\\CallbackTransformer` class:: - // src/AppBundle/Form/Type/TaskType.php - namespace AppBundle\Form\Type; + // src/Form/Type/TaskType.php + namespace App\Form\Type; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\FormBuilderInterface; @@ -120,10 +120,10 @@ issue number. Start by setting up the text field like normal:: - // src/AppBundle/Form/Type/TaskType.php - namespace AppBundle\Form\Type; + // src/Form/Type/TaskType.php + namespace App\Form\Type; - use AppBundle\Entity\Task; + use App\Entity\Task; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -161,10 +161,10 @@ complex, creating a new transformer class will keep the ``TaskType`` form class Create an ``IssueToNumberTransformer`` class: it will be responsible for converting to and from the issue number and the ``Issue`` object:: - // src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.php - namespace AppBundle\Form\DataTransformer; + // src/Form/DataTransformer/IssueToNumberTransformer.php + namespace App\Form\DataTransformer; - use AppBundle\Entity\Issue; + use App\Entity\Issue; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; @@ -250,10 +250,10 @@ Next, you need to use the ``IssueToNumberTransformer`` object inside if ``TaskTy and add it to the ``issue`` field. No problem! Just add a ``__construct()`` method and type-hint the new class:: - // src/AppBundle/Form/Type/TaskType.php - namespace AppBundle\Form\Type; + // src/Form/Type/TaskType.php + namespace App\Form\Type; - use AppBundle\Form\DataTransformer\IssueToNumberTransformer; + use App\Form\DataTransformer\IssueToNumberTransformer; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -332,10 +332,10 @@ that does this automatically. First, create the custom field type class:: - // src/AppBundle/Form/IssueSelectorType.php - namespace AppBundle\Form; + // src/Form/IssueSelectorType.php + namespace App\Form; - use AppBundle\Form\DataTransformer\IssueToNumberTransformer; + use App\Form\DataTransformer\IssueToNumberTransformer; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -374,10 +374,10 @@ have the data transformer *and* a nice default value for the ``invalid_message`` As long as you're using :ref:`autowire ` and :ref:`autoconfigure `, you can start using the form immediately:: - // src/AppBundle/Form/Type/TaskType.php - namespace AppBundle\Form\Type; + // src/Form/Type/TaskType.php + namespace App\Form\Type; - use AppBundle\Form\DataTransformer\IssueToNumberTransformer; + use App\Form\DataTransformer\IssueToNumberTransformer; use Symfony\Component\Form\Extension\Core\Type\TextareaType; // ... diff --git a/form/direct_submit.rst b/form/direct_submit.rst index 855043affc5..d80a96e8db3 100644 --- a/form/direct_submit.rst +++ b/form/direct_submit.rst @@ -10,7 +10,7 @@ submissions:: use Symfony\Component\HttpFoundation\Request; // ... - public function newAction(Request $request) + public function new(Request $request) { $form = $this->createFormBuilder() // ... @@ -47,7 +47,7 @@ method, pass the submitted data directly to use Symfony\Component\HttpFoundation\Request; // ... - public function newAction(Request $request) + public function new(Request $request) { $form = $this->createFormBuilder() // ... diff --git a/form/dynamic_form_modification.rst b/form/dynamic_form_modification.rst index b0f9b1400df..4f3a8b84a26 100644 --- a/form/dynamic_form_modification.rst +++ b/form/dynamic_form_modification.rst @@ -35,10 +35,10 @@ Customizing your Form Based on the Underlying Data Before starting with dynamic form generation, remember what a bare form class looks like:: - // src/AppBundle/Form/Type/ProductType.php - namespace AppBundle\Form\Type; + // src/Form/Type/ProductType.php + namespace App\Form\Type; - use AppBundle\Entity\Product; + use App\Entity\Product; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -83,8 +83,8 @@ Adding an Event Listener to a Form Class So, instead of directly adding that ``name`` widget, the responsibility of creating that particular field is delegated to an event listener:: - // src/AppBundle/Form/Type/ProductType.php - namespace AppBundle\Form\Type; + // src/Form/Type/ProductType.php + namespace App\Form\Type; // ... use Symfony\Component\Form\FormEvent; @@ -141,8 +141,8 @@ For better reusability or if there is some heavy logic in your event listener, you can also move the logic for creating the ``name`` field to an :ref:`event subscriber `:: - // src/AppBundle/Form/EventListener/AddNameFieldSubscriber.php - namespace AppBundle\Form\EventListener; + // src/Form/EventListener/AddNameFieldSubscriber.php + namespace App\Form\EventListener; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; @@ -171,11 +171,11 @@ you can also move the logic for creating the ``name`` field to an Great! Now use that in your form class:: - // src/AppBundle/Form/Type/ProductType.php - namespace AppBundle\Form\Type; + // src/Form/Type/ProductType.php + namespace App\Form\Type; // ... - use AppBundle\Form\EventListener\AddNameFieldSubscriber; + use App\Form\EventListener\AddNameFieldSubscriber; class ProductType extends AbstractType { @@ -205,14 +205,13 @@ Creating the Form Type Using an event listener, your form might look like this:: - // src/AppBundle/Form/Type/FriendMessageFormType.php - namespace AppBundle\Form\Type; + // src/Form/Type/FriendMessageFormType.php + namespace App\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; - use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; @@ -231,51 +230,49 @@ Using an event listener, your form might look like this:: } The problem is now to get the current user and create a choice field that -contains only this user's friends. +contains only this user's friends. This can be done injecting the ``Security`` +service into the form type so you can get the current user object:: -Luckily it is pretty easy to inject a service inside of the form. This can be -done in the constructor:: + use Symfony\Component\Security\Core\Security; - private $tokenStorage; + private $security; - public function __construct(TokenStorageInterface $tokenStorage) + public function __construct(Security $security) { - $this->tokenStorage = $tokenStorage; + $this->security = $security; } .. note:: - You might wonder, now that you have access to the User (through the token - storage), why not just use it directly in ``buildForm()`` and omit the - event listener? This is because doing so in the ``buildForm()`` method - would result in the whole form type being modified and not just this - one form instance. This may not usually be a problem, but technically - a single form type could be used on a single request to create many forms - or fields. + You might wonder, now that you have access to the ``User`` object, why not + just use it directly in ``buildForm()`` and omit the event listener? This is + because doing so in the ``buildForm()`` method would result in the whole + form type being modified and not just this one form instance. This may not + usually be a problem, but technically a single form type could be used on a + single request to create many forms or fields. Customizing the Form Type ~~~~~~~~~~~~~~~~~~~~~~~~~ -Now that you have all the basics in place you can take advantage of the ``TokenStorageInterface`` -and fill in the listener logic:: +Now that you have all the basics in place you can complete the listener logic:: - // src/AppBundle/Form/Type/FriendMessageFormType.php + // src/Form/Type/FriendMessageFormType.php - use AppBundle\Entity\User; + use App\Entity\User; use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; - use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + use Symfony\Component\Security\Core\Security; // ... class FriendMessageFormType extends AbstractType { - private $tokenStorage; + private $security; - public function __construct(TokenStorageInterface $tokenStorage) + public function __construct(Security $security) { - $this->tokenStorage = $tokenStorage; + $this->security = $security; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -286,7 +283,7 @@ and fill in the listener logic:: ; // grab the user, do a quick sanity check that one exists - $user = $this->tokenStorage->getToken()->getUser(); + $user = $this->security->getUser(); if (!$user) { throw new \LogicException( 'The FriendMessageFormType cannot be used without an authenticated user!' @@ -301,13 +298,12 @@ and fill in the listener logic:: $formOptions = array( 'class' => User::class, 'choice_label' => 'fullName', - 'query_builder' => function (EntityRepository $er) use ($user) { + 'query_builder' => function (EntityRepository $userRepository) use ($user) { // build a custom query - // return $er->createQueryBuilder('u')->addOrderBy('fullName', 'DESC'); + // return $userRepository->createQueryBuilder('u')->addOrderBy('fullName', 'DESC'); // or call a method on your repository that returns the query builder - // the $er is an instance of your UserRepository - // return $er->createOrderByFullNameQueryBuilder(); + // return $userRepository->createOrderByFullNameQueryBuilder(); }, ); @@ -331,11 +327,8 @@ Using the Form If you're using :ref:`autowire ` and :ref:`autoconfigure `, your form is ready to be used! - -.. tip:: - - If you're not using autowire and autoconfigure, see :doc:`/form/form_dependencies` - for how to register your form type as a service. +Otherwise, see :doc:`/form/form_dependencies` to learn how to register your form +type as a service. In a controller, create the form like normal:: @@ -343,7 +336,7 @@ In a controller, create the form like normal:: class FriendMessageController extends Controller { - public function newAction(Request $request) + public function new(Request $request) { $form = $this->createForm(FriendMessageFormType::class); @@ -375,8 +368,8 @@ will need the correct options in order for validation to pass. The meetup is passed as an entity field to the form. So we can access each sport like this:: - // src/AppBundle/Form/Type/SportMeetupType.php - namespace AppBundle\Form\Type; + // src/Form/Type/SportMeetupType.php + namespace App\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -391,7 +384,7 @@ sport like this:: { $builder ->add('sport', EntityType::class, array( - 'class' => 'AppBundle:Sport', + 'class' => 'App\Entity\Sport', 'placeholder' => '', )) ; @@ -408,7 +401,7 @@ sport like this:: $positions = null === $sport ? array() : $sport->getAvailablePositions(); $form->add('position', EntityType::class, array( - 'class' => 'AppBundle:Position', + 'class' => 'App\Entity\Position', 'placeholder' => '', 'choices' => $positions, )); @@ -441,13 +434,13 @@ new field automatically and map it to the submitted client data. The type would now look like:: - // src/AppBundle/Form/Type/SportMeetupType.php - namespace AppBundle\Form\Type; + // src/Form/Type/SportMeetupType.php + namespace App\Form\Type; - // ... + use App\Entity\Sport; use Symfony\Component\Form\FormInterface; use Symfony\Bridge\Doctrine\Form\Type\EntityType; - use AppBundle\Entity\Sport; + // ... class SportMeetupType extends AbstractType { @@ -455,7 +448,7 @@ The type would now look like:: { $builder ->add('sport', EntityType::class, array( - 'class' => 'AppBundle:Sport', + 'class' => 'App\Entity\Sport', 'placeholder' => '', )); ; @@ -464,7 +457,7 @@ The type would now look like:: $positions = null === $sport ? array() : $sport->getAvailablePositions(); $form->add('position', EntityType::class, array( - 'class' => 'AppBundle:Position', + 'class' => 'App\Entity\Position', 'placeholder' => '', 'choices' => $positions, )); @@ -511,18 +504,18 @@ One piece that is still missing is the client-side updating of your form after the sport is selected. This should be handled by making an AJAX call back to your application. Assume that you have a sport meetup creation controller:: - // src/AppBundle/Controller/MeetupController.php - namespace AppBundle\Controller; + // src/Controller/MeetupController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; - use AppBundle\Entity\SportMeetup; - use AppBundle\Form\Type\SportMeetupType; + use App\Entity\SportMeetup; + use App\Form\Type\SportMeetupType; // ... class MeetupController extends Controller { - public function createAction(Request $request) + public function create(Request $request) { $meetup = new SportMeetup(); $form = $this->createForm(SportMeetupType::class, $meetup); @@ -547,7 +540,7 @@ field according to the current selection in the ``sport`` field: .. code-block:: html+twig - {# app/Resources/views/meetup/create.html.twig #} + {# templates/meetup/create.html.twig #} {{ form_start(form) }} {{ form_row(form.sport) }} {# + + + + +Then, get the value of the CSRF token in the controller action and use the +:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::isCsrfTokenValid` +to check its validity:: + + use Symfony\Component\HttpFoundation\Request; + // ... + + public function delete(Request $request) + { + $submittedToken = $request->request->get('token'); + + // 'delete-item' is the same value used in the template to generate the token + if ($this->isCsrfTokenValid('delete-item', $submittedToken)) { + // ... do something, like deleting an object + } + } + +.. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery +.. _`Login CSRF attacks`: https://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests diff --git a/security/custom_authentication_provider.rst b/security/custom_authentication_provider.rst index e6ced2a728a..7b617aae9d4 100644 --- a/security/custom_authentication_provider.rst +++ b/security/custom_authentication_provider.rst @@ -61,8 +61,8 @@ this data across the security context. First, you'll create your token class. This will allow the passing of all relevant information to your authentication provider:: - // src/AppBundle/Security/Authentication/Token/WsseUserToken.php - namespace AppBundle\Security\Authentication\Token; + // src/Security/Authentication/Token/WsseUserToken.php + namespace App\Security\Authentication\Token; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; @@ -105,8 +105,8 @@ A security listener should handle the :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` event, and set an authenticated token in the token storage if successful:: - // src/AppBundle/Security/Firewall/WsseListener.php - namespace AppBundle\Security\Firewall; + // src/Security/Firewall/WsseListener.php + namespace App\Security\Firewall; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -114,7 +114,7 @@ set an authenticated token in the token storage if successful:: use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Firewall\ListenerInterface; - use AppBundle\Security\Authentication\Token\WsseUserToken; + use App\Security\Authentication\Token\WsseUserToken; class WsseListener implements ListenerInterface { @@ -199,8 +199,8 @@ Namely, the provider will verify the ``Created`` header value is valid within five minutes, the ``Nonce`` header value is unique within five minutes, and the ``PasswordDigest`` header value matches with the user's password:: - // src/AppBundle/Security/Authentication/Provider/WsseProvider.php - namespace AppBundle\Security\Authentication\Provider; + // src/Security/Authentication/Provider/WsseProvider.php + namespace App\Security\Authentication\Provider; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; @@ -208,7 +208,7 @@ the ``PasswordDigest`` header value matches with the user's password:: use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\NonceExpiredException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - use AppBundle\Security\Authentication\Token\WsseUserToken; + use App\Security\Authentication\Token\WsseUserToken; class WsseProvider implements AuthenticationProviderInterface { @@ -304,16 +304,16 @@ provider and any configuration options available for it. First, you must create a class which implements :class:`Symfony\\Bundle\\SecurityBundle\\DependencyInjection\\Security\\Factory\\SecurityFactoryInterface`:: - // src/AppBundle/DependencyInjection/Security/Factory/WsseFactory.php - namespace AppBundle\DependencyInjection\Security\Factory; + // src/DependencyInjection/Security/Factory/WsseFactory.php + namespace App\DependencyInjection\Security\Factory; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface; - use AppBundle\Security\Authentication\Provider\WsseProvider; - use AppBundle\Security\Firewall\WsseListener; + use App\Security\Authentication\Provider\WsseProvider; + use App\Security\Firewall\WsseListener; class WsseFactory implements SecurityFactoryInterface { @@ -392,42 +392,42 @@ Configuration It's time to see your authentication provider in action. You will need to do a few things in order to make this work. The first thing is to add the services above to the DI container. Your factory class above makes reference -to service ids that may not exist yet: ``AppBundle\Security\Authentication\Provider\WsseProvider`` and -``AppBundle\Security\Firewall\WsseListener``. It's time to define those services. +to service ids that may not exist yet: ``App\Security\Authentication\Provider\WsseProvider`` and +``App\Security\Firewall\WsseListener``. It's time to define those services. .. configuration-block:: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - AppBundle\Security\Authentication\Provider\WsseProvider: + App\Security\Authentication\Provider\WsseProvider: arguments: $cachePool: '@cache.app' public: false - AppBundle\Security\Firewall\WsseListener: + App\Security\Firewall\WsseListener: arguments: ['@security.token_storage', '@security.authentication.manager'] public: false .. code-block:: xml - + - - @@ -438,9 +438,9 @@ to service ids that may not exist yet: ``AppBundle\Security\Authentication\Provi .. code-block:: php - // app/config/services.php - use AppBundle\Security\Authentication\Provider\WsseProvider; - use AppBundle\Security\Firewall\WsseListener; + // config/services.php + use App\Security\Authentication\Provider\WsseProvider; + use App\Security\Firewall\WsseListener; use Symfony\Component\DependencyInjection\Reference; $container->register(WsseProvider::class) @@ -455,24 +455,23 @@ to service ids that may not exist yet: ``AppBundle\Security\Authentication\Provi ->setPublic(false); Now that your services are defined, tell your security context about your -factory in your bundle class:: +factory in the kernel:: - // src/AppBundle/AppBundle.php - namespace AppBundle; + // src/Kernel.php + namespace App; - use AppBundle\DependencyInjection\Security\Factory\WsseFactory; - use Symfony\Component\HttpKernel\Bundle\Bundle; - use Symfony\Component\DependencyInjection\ContainerBuilder; + use App\DependencyInjection\Security\Factory\WsseFactory; + // ... - class AppBundle extends Bundle + class Kernel extends BaseKernel { public function build(ContainerBuilder $container) { - parent::build($container); - $extension = $container->getExtension('security'); $extension->addSecurityListenerFactory(new WsseFactory()); } + + // ... } You are finished! You can now define parts of your app as under WSSE protection. @@ -481,7 +480,7 @@ You are finished! You can now define parts of your app as under WSSE protection. .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -493,7 +492,7 @@ You are finished! You can now define parts of your app as under WSSE protection. .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -567,7 +566,7 @@ contain a ``lifetime`` key, set to 5 minutes (300 seconds) unless otherwise set in the configuration. Pass this argument to your authentication provider in order to put it to use:: - use AppBundle\Security\Authentication\Provider\WsseProvider; + use App\Security\Authentication\Provider\WsseProvider; class WsseFactory implements SecurityFactoryInterface { @@ -597,7 +596,7 @@ set to any desirable value per firewall. .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -609,7 +608,7 @@ set to any desirable value per firewall. .. code-block:: xml - + loadFromExtension('security', array( // ... diff --git a/security/custom_password_authenticator.rst b/security/custom_password_authenticator.rst index cdfa7511123..6f038ee434e 100644 --- a/security/custom_password_authenticator.rst +++ b/security/custom_password_authenticator.rst @@ -21,8 +21,8 @@ First, create a new class that implements Eventually, this will allow you to create custom logic for authenticating the user:: - // src/AppBundle/Security/TimeAuthenticator.php - namespace AppBundle\Security; + // src/Security/TimeAuthenticator.php + namespace App\Security; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -132,10 +132,10 @@ inside of it. Inside this method, the password encoder is needed to check the password's validity:: - $isPasswordValid = $this->encoder->isPasswordValid($user, $token->getCredentials()); + $isPasswordValid = $this->encoder->isPasswordValid($user, $token->getCredentials()); This is a service that is already available in Symfony and it uses the password algorithm -that is configured in the security configuration (e.g. ``security.yml``) under +that is configured in the security configuration (e.g. ``security.yaml``) under the ``encoders`` key. Below, you'll see how to inject that into the ``TimeAuthenticator``. .. _security-password-authenticator-config: @@ -144,7 +144,7 @@ Configuration ------------- Now, make sure your ``TimeAuthenticator`` is registered as as service. If you're -using the :ref:`default services.yml configuration `, +using the :ref:`default services.yaml configuration `, that happens automatically. Finally, activate the service in the ``firewalls`` section of the security configuration @@ -154,7 +154,7 @@ using the ``simple_form`` key: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -163,13 +163,13 @@ using the ``simple_form`` key: pattern: ^/admin # ... simple_form: - authenticator: AppBundle\Security\TimeAuthenticator + authenticator: App\Security\TimeAuthenticator check_path: login_check login_path: login .. code-block:: xml - + - @@ -192,10 +192,10 @@ using the ``simple_form`` key: .. code-block:: php - // app/config/security.php + // config/packages/security.php // ... - use AppBundle\Security\TimeAuthenticator; + use App\Security\TimeAuthenticator; $container->loadFromExtension('security', array( 'firewalls' => array( @@ -203,7 +203,7 @@ using the ``simple_form`` key: 'pattern' => '^/admin', 'simple_form' => array( 'provider' => ..., - 'authenticator' => AppBundle\Security\TimeAuthenticator::class, + 'authenticator' => App\Security\TimeAuthenticator::class, 'check_path' => 'login_check', 'login_path' => 'login', ), diff --git a/security/custom_provider.rst b/security/custom_provider.rst index 66d3f846720..ae630e40e75 100644 --- a/security/custom_provider.rst +++ b/security/custom_provider.rst @@ -35,8 +35,8 @@ method. This is how your ``WebserviceUser`` class looks in action:: - // src/AppBundle/Security/User/WebserviceUser.php - namespace AppBundle\Security\User; + // src/Security/User/WebserviceUser.php + namespace App\Security\User; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\EquatableInterface; @@ -120,10 +120,10 @@ more details, see :class:`Symfony\\Component\\Security\\Core\\User\\UserProvider Here's an example of how this might look:: - // src/AppBundle/Security/User/WebserviceUserProvider.php - namespace AppBundle\Security\User; + // src/Security/User/WebserviceUserProvider.php + namespace App\Security\User; - use AppBundle\Security\User\WebserviceUser; + use App\Security\User\WebserviceUser; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; @@ -171,31 +171,31 @@ Create a Service for the User Provider -------------------------------------- Now you make the user provider available as a service. If you're using the -:ref:`default services.yml configuration `, +:ref:`default services.yaml configuration `, this happens automatically. -Modify ``security.yml`` ------------------------ +Modify ``security.yaml`` +------------------------ Everything comes together in your security configuration. Add the user provider -to the list of providers in the "security" section. Choose a name for the user provider +to the list of providers in the "security" config. Choose a name for the user provider (e.g. "webservice") and mention the ``id`` of the service you just defined. .. configuration-block:: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... providers: webservice: - id: AppBundle\Security\User\WebserviceUserProvider + id: App\Security\User\WebserviceUserProvider .. code-block:: xml - + - + .. code-block:: php - // app/config/security.php - use AppBundle\Security\User\WebserviceUserProvider; + // config/packages/security.php + use App\Security\User\WebserviceUserProvider; $container->loadFromExtension('security', array( // ... @@ -233,16 +233,16 @@ users, e.g. by filling in a login form. You can do this by adding a line to the .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... encoders: - AppBundle\Security\User\WebserviceUser: bcrypt + App\Security\User\WebserviceUser: bcrypt .. code-block:: xml - + - .. code-block:: php - // app/config/security.php - use AppBundle\Security\User\WebserviceUser; + // config/packages/security.php + use App\Security\User\WebserviceUser; $container->loadFromExtension('security', array( // ... @@ -282,7 +282,7 @@ is compared to the hashed password returned by your ``getPassword()`` method. Symfony uses a specific method to combine the salt and encode the password before comparing it to your encoded password. If ``getSalt()`` returns nothing, then the submitted password is simply encoded using the algorithm - you specify in ``security.yml``. If a salt *is* specified, then the following + you specify in ``security.yaml``. If a salt *is* specified, then the following value is created and *then* hashed via the algorithm:: $password.'{'.$salt.'}' @@ -301,18 +301,18 @@ is compared to the hashed password returned by your ``getPassword()`` method. .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... encoders: - AppBundle\Security\User\WebserviceUser: + App\Security\User\WebserviceUser: algorithm: bcrypt cost: 12 .. code-block:: xml - + - @@ -331,8 +331,8 @@ is compared to the hashed password returned by your ``getPassword()`` method. .. code-block:: php - // app/config/security.php - use AppBundle\Security\User\WebserviceUser; + // config/packages/security.php + use App\Security\User\WebserviceUser; $container->loadFromExtension('security', array( // ... diff --git a/security/entity_provider.rst b/security/entity_provider.rst index c0fd0c39e28..4fcf1176e56 100644 --- a/security/entity_provider.rst +++ b/security/entity_provider.rst @@ -12,19 +12,10 @@ you how to load your users from the database via a Doctrine entity. Introduction ------------ -.. tip:: - - Before you start, you should check out `FOSUserBundle`_. This external - bundle allows you to load users from the database (like you'll learn here) - *and* gives you built-in routes & controllers for things like login, - registration and forgot password. But, if you need to heavily customize - your user system *or* if you want to learn how things work, this tutorial - is even better. - Loading users via a Doctrine entity has 2 basic steps: #. :ref:`Create your User entity ` -#. :ref:`Configure security.yml to load from your entity ` +#. :ref:`Configure security.yaml to load from your entity ` Afterwards, you can learn more about :ref:`forbidding inactive users `, :ref:`using a custom query ` @@ -36,19 +27,25 @@ and :ref:`user serialization to the session ` 1) Create your User Entity -------------------------- -For this entry, suppose that you already have a ``User`` entity inside an -``AppBundle`` with the following fields: ``id``, ``username``, ``password``, +Before you begin, first make sure you install the Security component: + +.. code-block:: terminal + + $ composer require security + +For this entry, suppose that you already have a ``User`` entity +with the following fields: ``id``, ``username``, ``password``, ``email`` and ``isActive``:: - // src/AppBundle/Entity/User.php - namespace AppBundle\Entity; + // src/Entity/User.php + namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\UserInterface; /** * @ORM\Table(name="app_users") - * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository") + * @ORM\Entity(repositoryClass="App\Repository\UserRepository") */ class User implements UserInterface, \Serializable { @@ -151,7 +148,8 @@ Next, make sure to :ref:`create the database table `). -On each request, the ``id`` is used to query for a fresh ``User`` object -from the database. +you only need a few fields (the ones shown above plus a few extra if you added +other important fields to your user entity). On each request, the ``id`` is used +to query for a fresh ``User`` object from the database. Want to know more? See :ref:`security-serialize-equatable`. @@ -196,7 +193,7 @@ Want to know more? See :ref:`security-serialize-equatable`. ---------------------------------------------- Now that you have a ``User`` entity that implements ``UserInterface``, you -just need to tell Symfony's security system about it in ``security.yml``. +just need to tell Symfony's security system about it in ``security.yaml``. In this example, the user will enter their username and password via HTTP basic authentication. Symfony will query for a ``User`` entity matching @@ -206,10 +203,10 @@ the username and then check the password (more on passwords in a moment): .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: encoders: - AppBundle\Entity\User: + App\Entity\User: algorithm: bcrypt # ... @@ -217,7 +214,7 @@ the username and then check the password (more on passwords in a moment): providers: our_db_provider: entity: - class: AppBundle:User + class: App\Entity\User property: username # if you're using multiple entity managers # manager_name: customer @@ -232,7 +229,7 @@ the username and then check the password (more on passwords in a moment): .. code-block:: xml - + - + - + @@ -261,8 +258,8 @@ the username and then check the password (more on passwords in a moment): .. code-block:: php - // app/config/security.php - use AppBundle\Entity\User; + // config/packages/security.php + use App\Entity\User; $container->loadFromExtension('security', array( 'encoders' => array( @@ -276,7 +273,7 @@ the username and then check the password (more on passwords in a moment): 'providers' => array( 'our_db_provider' => array( 'entity' => array( - 'class' => 'AppBundle:User', + 'class' => User::class, 'property' => 'username', ), ), @@ -295,7 +292,7 @@ the username and then check the password (more on passwords in a moment): First, the ``encoders`` section tells Symfony to expect that the passwords in the database will be encoded using ``bcrypt``. Second, the ``providers`` section creates a "user provider" called ``our_db_provider`` that knows to -query from your ``AppBundle:User`` entity by the ``username`` property. The +query from your ``App\Entity\User`` entity by the ``username`` property. The name ``our_db_provider`` isn't important: it just needs to match the value of the ``provider`` key under your firewall. Or, if you don't set the ``provider`` key under your firewall, the first "user provider" is automatically used. @@ -335,6 +332,12 @@ and password ``admin`` (which has been encoded). Forbid Inactive Users (AdvancedUserInterface) --------------------------------------------- +.. versionadded:: 4.1 + The ``AdvancedUserInterface`` class was deprecated in Symfony 4.1 and no + alternative is provided. If you need this functionality in your application, + implement :doc:`a custom user checker ` that + performs the needed checks. + If a User's ``isActive`` property is set to ``false`` (i.e. ``is_active`` is 0 in the database), the user will still be able to login to the site normally. This is easily fixable. @@ -344,7 +347,7 @@ To exclude inactive users, change your ``User`` class to implement This extends :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface`, so you only need the new interface:: - // src/AppBundle/Entity/User.php + // src/Entity/User.php use Symfony\Component\Security\Core\User\AdvancedUserInterface; // ... @@ -434,8 +437,8 @@ To do this, make your ``UserRepository`` implement a special :class:`Symfony\\Bridge\\Doctrine\\Security\\User\\UserLoaderInterface`. This interface only requires one method: ``loadUserByUsername($username)``:: - // src/AppBundle/Repository/UserRepository.php - namespace AppBundle\Repository; + // src/Repository/UserRepository.php + namespace App\Repository; use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface; use Doctrine\ORM\EntityRepository; @@ -453,30 +456,25 @@ interface only requires one method: ``loadUserByUsername($username)``:: } } -.. tip:: - - Don't forget to add the repository class to the - :doc:`mapping definition of your entity `. - To finish this, just remove the ``property`` key from the user provider in -``security.yml``: +``security.yaml``: .. configuration-block:: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... providers: our_db_provider: entity: - class: AppBundle:User + class: App\Entity\User .. code-block:: xml - + - + .. code-block:: php - // app/config/security.php + // config/packages/security.php + use App\Entity\User; + $container->loadFromExtension('security', array( // ... 'providers' => array( 'our_db_provider' => array( 'entity' => array( - 'class' => 'AppBundle:User', + 'class' => User::class, ), ), ), diff --git a/security/expressions.rst b/security/expressions.rst index 6d946e2523d..f6ad69d4c48 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -15,7 +15,7 @@ accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object:: use Symfony\Component\ExpressionLanguage\Expression; // ... - public function indexAction() + public function index() { $this->denyAccessUnlessGranted(new Expression( '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' @@ -70,17 +70,20 @@ Additionally, you have access to a number of functions inside the expression: The ``is_remember_me()`` and ``is_authenticated_fully()`` functions are *similar* to using ``IS_AUTHENTICATED_REMEMBERED`` and ``IS_AUTHENTICATED_FULLY`` with the ``isGranted()`` function - but they are **not** the same. The - following shows the difference:: + following controller snippet shows the difference:: use Symfony\Component\ExpressionLanguage\Expression; + use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; // ... - $authorizationChecker = $this->get('security.authorization_checker'); - $access1 = $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED'); + public function index(AuthorizationCheckerInterface $authorizationChecker) + { + $access1 = $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED'); - $access2 = $authorizationChecker->isGranted(new Expression( - 'is_remember_me() or is_fully_authenticated()' - )); + $access2 = $authorizationChecker->isGranted(new Expression( + 'is_remember_me() or is_fully_authenticated()' + )); + } Here, ``$access1`` and ``$access2`` will be the same value. Unlike the behavior of ``IS_AUTHENTICATED_REMEMBERED`` and ``IS_AUTHENTICATED_FULLY``, diff --git a/security/firewall_restriction.rst b/security/firewall_restriction.rst index 96d4a1e37f9..82dc7c107ff 100644 --- a/security/firewall_restriction.rst +++ b/security/firewall_restriction.rst @@ -23,7 +23,7 @@ matches the configured ``pattern``. .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml # ... security: @@ -34,7 +34,7 @@ matches the configured ``pattern``. .. code-block:: xml - + loadFromExtension('security', array( @@ -80,7 +80,7 @@ only initialize if the host from the request matches against the configuration. .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml # ... security: @@ -91,7 +91,7 @@ only initialize if the host from the request matches against the configuration. .. code-block:: xml - + loadFromExtension('security', array( @@ -138,7 +138,7 @@ the provided HTTP methods. .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml # ... security: @@ -149,7 +149,7 @@ the provided HTTP methods. .. code-block:: xml - + loadFromExtension('security', array( diff --git a/security/force_https.rst b/security/force_https.rst index f06136a5255..124fdcb704c 100644 --- a/security/force_https.rst +++ b/security/force_https.rst @@ -13,7 +13,7 @@ to use HTTPS then you could use the following configuration: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -22,7 +22,7 @@ to use HTTPS then you could use the following configuration: .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -61,7 +61,7 @@ role: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -70,7 +70,7 @@ role: .. code-block:: xml - + loadFromExtension('security', array( // ... diff --git a/security/form_login.rst b/security/form_login.rst index 211d570be09..da7f8559ed8 100644 --- a/security/form_login.rst +++ b/security/form_login.rst @@ -42,7 +42,7 @@ a relative/absolute URL or a Symfony route name: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -54,7 +54,7 @@ a relative/absolute URL or a Symfony route name: .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -99,7 +99,7 @@ previously requested URL and always redirect to the default page: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -111,7 +111,7 @@ previously requested URL and always redirect to the default page: .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -168,7 +168,7 @@ Defining the redirect URL via POST using a hidden form field: .. code-block:: html+twig - {# app/Resources/views/security/login.html.twig #} + {# templates/security/login.html.twig #}
    {# ... #} @@ -178,7 +178,7 @@ Defining the redirect URL via POST using a hidden form field: .. code-block:: html+php - + // ... @@ -198,7 +198,7 @@ parameter is included in the request, you may use the value of the .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -211,7 +211,7 @@ parameter is included in the request, you may use the value of the .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -264,7 +264,7 @@ option to define a new target via a relative/absolute URL or a Symfony route nam .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -277,7 +277,7 @@ option to define a new target via a relative/absolute URL or a Symfony route nam .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -322,7 +322,7 @@ This option can also be set via the ``_failure_path`` request parameter: .. code-block:: html+twig - {# app/Resources/views/security/login.html.twig #} + {# templates/security/login.html.twig #} {# ... #} @@ -332,7 +332,7 @@ This option can also be set via the ``_failure_path`` request parameter: .. code-block:: html+php - + @@ -351,7 +351,7 @@ redirects can be customized using the ``target_path_parameter`` and .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -364,7 +364,7 @@ redirects can be customized using the ``target_path_parameter`` and .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -411,7 +411,7 @@ are now fully customized: .. code-block:: html+twig - {# app/Resources/views/security/login.html.twig #} + {# templates/security/login.html.twig #} {# ... #} @@ -422,7 +422,7 @@ are now fully customized: .. code-block:: html+php - + @@ -448,6 +448,3 @@ remove this variable, it's better to use the // equivalent to: // $targetPath = $request->getSession()->get('_security.'.$providerKey.'.target_path'); - -.. versionadded:: 3.1 - The ``TargetPathTrait`` was introduced in Symfony 3.1. diff --git a/security/form_login_setup.rst b/security/form_login_setup.rst index ca7a64d28b3..ad76bef4033 100644 --- a/security/form_login_setup.rst +++ b/security/form_login_setup.rst @@ -18,7 +18,7 @@ First, enable form login under your firewall: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -31,7 +31,7 @@ First, enable form login under your firewall: .. code-block:: xml - + loadFromExtension('security', array( 'firewalls' => array( 'main' => array( @@ -72,8 +72,8 @@ Now, when the security system initiates the authentication process, it will redirect the user to the login form ``/login``. Implementing this login form is your job. First, create a new ``SecurityController`` inside a bundle:: - // src/AppBundle/Controller/SecurityController.php - namespace AppBundle\Controller; + // src/Controller/SecurityController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -88,7 +88,7 @@ configuration (``login``): .. code-block:: php-annotations - // src/AppBundle/Controller/SecurityController.php + // src/Controller/SecurityController.php // ... use Symfony\Component\HttpFoundation\Request; @@ -99,21 +99,21 @@ configuration (``login``): /** * @Route("/login", name="login") */ - public function loginAction(Request $request) + public function login(Request $request) { } } .. code-block:: yaml - # app/config/routing.yml + # config/routes.yaml login: - path: /login - defaults: { _controller: AppBundle:Security:login } + path: /login + controller: App\Controller\SecurityController::login .. code-block:: xml - + - AppBundle:Security:login + App\Controller\SecurityController::login .. code-block:: php - // app/config/routing.php + // config/routes.php + use App\Controller\SecurityController; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('login', new Route('/login', array( - '_controller' => 'AppBundle:Security:login', + '_controller' => array(SecurityController::class, 'login'), ))); return $routes; -Great! Next, add the logic to ``loginAction()`` that displays the login form:: +Great! Next, add the logic to ``login()`` that displays the login form:: - // src/AppBundle/Controller/SecurityController.php + // src/Controller/SecurityController.php use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; - public function loginAction(Request $request, AuthenticationUtils $authenticationUtils) + public function login(Request $request, AuthenticationUtils $authenticationUtils) { // get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); @@ -160,8 +161,9 @@ Great! Next, add the logic to ``loginAction()`` that displays the login form:: .. note:: If you get an error that the ``$authenticationUtils`` argument is missing, - it's probably because you need to activate this new feature in Symfony 3.4. - See this :ref:`controller service argument note `. + it's probably because the controllers of your application are not defined as + services and tagged with the ``controller.service_arguments`` tag, as done + in the :ref:`default services.yaml configuration `. Don't let this controller confuse you. As you'll see in a moment, when the user submits the form, the security system automatically handles the form @@ -179,7 +181,7 @@ Finally, create the template: .. code-block:: html+twig - {# app/Resources/views/security/login.html.twig #} + {# templates/security/login.html.twig #} {# ... you will probably extend your base template, like base.html.twig #} {% if error %} @@ -204,7 +206,7 @@ Finally, create the template: .. code-block:: html+php - +
    getMessage() ?>
    @@ -235,7 +237,7 @@ Finally, create the template: The form can look like anything, but it usually follows some conventions: * The ```` element sends a ``POST`` request to the ``login`` route, since - that's what you configured under the ``form_login`` key in ``security.yml``; + that's what you configured under the ``form_login`` key in ``security.yaml``; * The username field has the name ``_username`` and the password field has the name ``_password``. @@ -247,8 +249,7 @@ The form can look like anything, but it usually follows some conventions: .. caution:: This login form is currently not protected against CSRF attacks. Read - :doc:`/security/csrf_in_login_form` on how to protect your login - form. + :doc:`/security/csrf` on how to protect your login form. And that's it! When you submit the form, the security system will automatically check the user's credentials and either authenticate the user or send the @@ -305,7 +306,7 @@ all URLs (including the ``/login`` URL), will cause a redirect loop: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml # ... access_control: @@ -313,7 +314,7 @@ all URLs (including the ``/login`` URL), will cause a redirect loop: .. code-block:: xml - + array( @@ -343,7 +344,7 @@ fixes the problem: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml # ... access_control: @@ -352,7 +353,7 @@ fixes the problem: .. code-block:: xml - + array( diff --git a/security/guard_authentication.rst b/security/guard_authentication.rst index 7f50fd775f0..a5b1f86a752 100644 --- a/security/guard_authentication.rst +++ b/security/guard_authentication.rst @@ -19,8 +19,8 @@ and configure a :doc:`user provider `. In this example, users are stored in the database via Doctrine, and each user has an ``apiKey`` property they use to access their account via the API:: - // src/AppBundle/Entity/User.php - namespace AppBundle\Entity; + // src/Entity/User.php + namespace App\Entity; use Symfony\Component\Security\Core\User\UserInterface; use Doctrine\ORM\Mapping as ORM; @@ -83,21 +83,21 @@ Next, make sure you've configured a "user provider" for the user: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... providers: your_db_provider: entity: - class: AppBundle:User + class: App\Entity\User property: apiKey # ... .. code-block:: xml - + - + @@ -118,14 +118,16 @@ Next, make sure you've configured a "user provider" for the user: .. code-block:: php - // app/config/security.php + // config/packages/security.php + use App\Entity\User; + $container->loadFromExtension('security', array( // ... 'providers' => array( 'your_db_provider' => array( 'entity' => array( - 'class' => 'AppBundle:User', + 'class' => User::class, ), ), ), @@ -150,8 +152,8 @@ To create a custom authentication system, just create a class and make it implem the simpler :class:`Symfony\\Component\\Security\\Guard\\AbstractGuardAuthenticator`. This requires you to implement several methods:: - // src/AppBundle/Security/TokenAuthenticator.php - namespace AppBundle\Security; + // src/Security/TokenAuthenticator.php + namespace App\Security; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; @@ -243,26 +245,22 @@ This requires you to implement several methods:: } } -.. versionadded:: 3.4 - ``AuthenticatorInterface`` was introduced in Symfony 3.4. In previous Symfony - versions, authenticators needed to implement ``GuardAuthenticatorInterface``. - Nice work! Each method is explained below: :ref:`The Guard Authenticator Methods`. Step 2) Configure the Authenticator ----------------------------------- To finish this, make sure your authenticator is registered as a service. If you're -using the :ref:`default services.yml configuration `, +using the :ref:`default services.yaml configuration `, that happens automatically. -Finally, configure your ``firewalls`` key in ``security.yml`` to use this authenticator: +Finally, configure your ``firewalls`` key in ``security.yaml`` to use this authenticator: .. configuration-block:: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -275,7 +273,7 @@ Finally, configure your ``firewalls`` key in ``security.yml`` to use this authen guard: authenticators: - - AppBundle\Security\TokenAuthenticator + - App\Security\TokenAuthenticator # if you want, disable storing the user in the session # stateless: true @@ -285,7 +283,7 @@ Finally, configure your ``firewalls`` key in ``security.yml`` to use this authen .. code-block:: xml - + - AppBundle\Security\TokenAuthenticator + App\Security\TokenAuthenticator @@ -312,10 +310,10 @@ Finally, configure your ``firewalls`` key in ``security.yml`` to use this authen .. code-block:: php - // app/config/security.php + // config/packages/security.php // .. - use AppBundle\Security\TokenAuthenticator; + use App\Security\TokenAuthenticator; $container->loadFromExtension('security', array( 'firewalls' => array( @@ -364,11 +362,6 @@ Each authenticator needs the following methods: authenticator should be used for this request (return ``true``) or if it should be skipped (return ``false``). - .. versionadded:: 3.4 - The ``supports()`` method was introduced in Symfony 3.4. In previous Symfony - versions, the authenticator could be skipped returning ``null`` in the - ``getCredentials()`` method. - **getCredentials(Request $request)** This will be called on *every* request and your job is to read the token (or whatever your "authentication" information is) from the request and return it. @@ -443,7 +436,7 @@ But, you can easily return a custom message by throwing a You can throw this from ``getCredentials()``, ``getUser()`` or ``checkCredentials()`` to cause a failure:: - // src/AppBundle/Security/TokenAuthenticator.php + // src/Security/TokenAuthenticator.php // ... use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; @@ -492,13 +485,14 @@ Adding CSRF Protection If you're using a Guard authenticator to build a login form and want to add CSRF protection, no problem! -First, :ref:`add the _csrf_token to your login template `. +First, check that :ref:`the csrf_protection option ` +is enabled and :ref:`add the _csrf_token field to your login form `. Then, type-hint ``CsrfTokenManagerInterface`` in your ``__construct()`` method -(or manually configure the ``security.csrf.token_manager`` service to be passed) -and add the following logic:: +(or manually configure the ``Symfony\Component\Security\Csrf\CsrfTokenManagerInterface`` +service to be passed) and add the following logic:: - // src/AppBundle/Security/ExampleFormAuthenticator.php + // src/Security/ExampleFormAuthenticator.php // ... use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; diff --git a/security/host_restriction.rst b/security/host_restriction.rst index b577a737585..0a07e4f6262 100644 --- a/security/host_restriction.rst +++ b/security/host_restriction.rst @@ -1,6 +1,5 @@ How to Restrict Firewalls to a Specific Host ============================================ -As of Symfony 2.5, more possibilities to restrict firewalls have been added. -You can read everything about all the possibilities (including ``host``) -in ":doc:`/security/firewall_restriction`". +There are more possibilities to restrict firewalls. You can read everything +about all the possibilities (including ``host``) in ":doc:`/security/firewall_restriction`". diff --git a/security/impersonating_user.rst b/security/impersonating_user.rst index 4fcccd8a52f..6c1c6b99d36 100644 --- a/security/impersonating_user.rst +++ b/security/impersonating_user.rst @@ -23,7 +23,7 @@ firewall listener: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -34,7 +34,7 @@ firewall listener: .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -66,11 +66,6 @@ firewall listener: ), )); -.. tip:: - - For using the ``switch_user`` listener in a ``stateless`` firewall, set the - ``switch_user.stateless`` option to ``true``. - To switch to another user, just add a query string with the ``_switch_user`` parameter and the username as the value to the current URL: @@ -110,16 +105,29 @@ In some cases you may need to get the object that represents the impersonator user rather than the impersonated user. Use the following snippet to iterate over the user's roles until you find one that a ``SwitchUserRole`` object:: + use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Role\SwitchUserRole; - $authorizationChecker = $this->get('security.authorization_checker'); - $tokenStorage = $this->get('security.token_storage'); + private $authorizationChecker; + private $tokenStorage; + + public function __construct(AuthorizationCheckerInterface $authorizationChecker, TokenStorageInterface $tokenStorage) + { + $this->authorizationChecker = $authorizationChecker; + $this->tokenStorage = $tokenStorage; + } - if ($authorizationChecker->isGranted('ROLE_PREVIOUS_ADMIN')) { - foreach ($tokenStorage->getToken()->getRoles() as $role) { - if ($role instanceof SwitchUserRole) { - $impersonatorUser = $role->getSource()->getUser(); - break; + public function someMethod() + { + // ... + + if ($authorizationChecker->isGranted('ROLE_PREVIOUS_ADMIN')) { + foreach ($tokenStorage->getToken()->getRoles() as $role) { + if ($role instanceof SwitchUserRole) { + $impersonatorUser = $role->getSource()->getUser(); + break; + } } } } @@ -134,7 +142,7 @@ setting: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -145,7 +153,7 @@ setting: .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -190,8 +198,8 @@ The :doc:`/session/locale_sticky_session` article does not update the locale when you impersonate a user. If you *do* want to be sure to update the locale when you switch users, add an event subscriber on this event:: - // src/AppBundle/EventListener/SwitchUserListener.php - namespace AppBundle\EventListener; + // src/EventListener/SwitchUserListener.php + namespace App\EventListener; use Symfony\Component\Security\Http\Event\SwitchUserEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -201,11 +209,15 @@ you switch users, add an event subscriber on this event:: { public function onSwitchUser(SwitchUserEvent $event) { - $event->getRequest()->getSession()->set( - '_locale', - // assuming your User has some getLocale() method - $event->getTargetUser()->getLocale() - ); + $request = $event->getRequest(); + + if ($request->hasSession() && ($session = $request->getSession)) { + $session->set( + '_locale', + // assuming your User has some getLocale() method + $event->getTargetUser()->getLocale() + ); + } } public static function getSubscribedEvents() @@ -217,7 +229,7 @@ you switch users, add an event subscriber on this event:: } } -That's it! If you're using the :ref:`default services.yml configuration `, +That's it! If you're using the :ref:`default services.yaml configuration `, Symfony will automatically discover your service and call ``onSwitchUser`` whenever a switch user occurs. diff --git a/security/json_login_setup.rst b/security/json_login_setup.rst index 3d1c12ffd40..ee2ebad7a46 100644 --- a/security/json_login_setup.rst +++ b/security/json_login_setup.rst @@ -11,7 +11,7 @@ First, enable the JSON login under your firewall: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -23,7 +23,7 @@ First, enable the JSON login under your firewall: .. code-block:: xml - + loadFromExtension('security', array( 'firewalls' => array( 'main' => array( @@ -66,7 +66,7 @@ path: .. code-block:: php-annotations - // src/AppBundle/Controller/SecurityController.php + // src/Controller/SecurityController.php // ... use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -78,21 +78,21 @@ path: /** * @Route("/login", name="login") */ - public function loginAction(Request $request) + public function login(Request $request) { } } .. code-block:: yaml - # app/config/routing.yml + # config/routes.yaml login: - path: /login - defaults: { _controller: AppBundle:Security:login } + path: /login + controller: App\Controller\SecurityController::login .. code-block:: xml - + - AppBundle:Security:login + App\Controller\SecurityController::login .. code-block:: php - // app/config/routing.php + // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; $routes = new RouteCollection(); $routes->add('login', new Route('/login', array( - '_controller' => 'AppBundle:Security:login', + '_controller' => 'App\Controller\SecurityController::login', ))); return $routes; @@ -152,7 +152,7 @@ The security configuration should be: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -166,7 +166,7 @@ The security configuration should be: .. code-block:: xml - + loadFromExtension('security', array( 'firewalls' => array( 'main' => array( diff --git a/security/ldap.rst b/security/ldap.rst index 5cdb9c9937f..185cb799268 100644 --- a/security/ldap.rst +++ b/security/ldap.rst @@ -56,7 +56,7 @@ An LDAP client can be simply configured using the built-in .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: Symfony\Component\Ldap\Ldap: arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter'] @@ -71,7 +71,7 @@ An LDAP client can be simply configured using the built-in .. code-block:: xml - + + loadFromExtension('security', array( @@ -326,7 +327,7 @@ Configuration example for form login .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -340,7 +341,7 @@ Configuration example for form login .. code-block:: xml - + loadFromExtension('security', array( @@ -380,7 +382,7 @@ Configuration example for HTTP Basic .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -394,7 +396,7 @@ Configuration example for HTTP Basic .. code-block:: xml - + loadFromExtension('security', array( @@ -433,7 +436,7 @@ Configuration example for form login and query_string .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -448,7 +451,7 @@ Configuration example for form login and query_string .. code-block:: xml - + loadFromExtension('security', array( diff --git a/security/multiple_guard_authenticators.rst b/security/multiple_guard_authenticators.rst index d7664cf2fb3..4544f22fbad 100644 --- a/security/multiple_guard_authenticators.rst +++ b/security/multiple_guard_authenticators.rst @@ -22,7 +22,7 @@ This is how your security configuration can look in action: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... firewalls: @@ -30,13 +30,13 @@ This is how your security configuration can look in action: anonymous: ~ guard: authenticators: - - AppBundle\Security\LoginFormAuthenticator - - AppBundle\Security\FacebookConnectAuthenticator - entry_point: AppBundle\Security\LoginFormAuthenticator + - App\Security\LoginFormAuthenticator + - App\Security\FacebookConnectAuthenticator + entry_point: App\Security\LoginFormAuthenticator .. code-block:: xml - + - - AppBundle\Security\LoginFormAuthenticator - AppBundle\Security\FacebookConnectAuthenticator + + App\Security\LoginFormAuthenticator + App\Security\FacebookConnectAuthenticator @@ -58,9 +58,9 @@ This is how your security configuration can look in action: .. code-block:: php - // app/config/security.php - use AppBundle\Security\LoginFormAuthenticator; - use AppBundle\Security\FacebookConnectAuthenticator; + // config/packages/security.php + use App\Security\LoginFormAuthenticator; + use App\Security\FacebookConnectAuthenticator; $container->loadFromExtension('security', array( // ... @@ -93,7 +93,7 @@ the solution is to split the configuration into two separate firewalls: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... firewalls: @@ -101,12 +101,12 @@ the solution is to split the configuration into two separate firewalls: pattern: ^/api/ guard: authenticators: - - AppBundle\Security\ApiTokenAuthenticator + - App\Security\ApiTokenAuthenticator default: anonymous: ~ guard: authenticators: - - AppBundle\Security\LoginFormAuthenticator + - App\Security\LoginFormAuthenticator access_control: - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api, roles: ROLE_API_USER } @@ -114,7 +114,7 @@ the solution is to split the configuration into two separate firewalls: .. code-block:: xml - + - AppBundle\Security\ApiTokenAuthenticator + App\Security\ApiTokenAuthenticator - AppBundle\Security\LoginFormAuthenticator + App\Security\LoginFormAuthenticator @@ -143,9 +143,9 @@ the solution is to split the configuration into two separate firewalls: .. code-block:: php - // app/config/security.php - use AppBundle\Security\ApiTokenAuthenticator; - use AppBundle\Security\LoginFormAuthenticator; + // config/packages/security.php + use App\Security\ApiTokenAuthenticator; + use App\Security\LoginFormAuthenticator; $container->loadFromExtension('security', array( // ... diff --git a/security/multiple_user_providers.rst b/security/multiple_user_providers.rst index dc26f528223..d21fcd968ce 100644 --- a/security/multiple_user_providers.rst +++ b/security/multiple_user_providers.rst @@ -1,17 +1,16 @@ How to Use multiple User Providers ================================== -Each authentication mechanism (e.g. HTTP Authentication, form login, etc) -uses exactly one user provider, and will use the first declared user provider -by default. But what if you want to specify a few users via configuration -and the rest of your users in the database? This is possible by creating -a new provider that chains the two together: +Each authentication mechanism (e.g. HTTP Authentication, form login, etc.) uses +exactly one user provider. But what if you want to specify a few users via +configuration and the rest of your users in the database? This is possible by +creating a new provider that chains the two together: .. configuration-block:: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: providers: chain_provider: @@ -22,11 +21,11 @@ a new provider that chains the two together: users: foo: { password: test } user_db: - entity: { class: AppBundle\Entity\User, property: username } + entity: { class: App\Entity\User, property: username } .. code-block:: xml - + - + .. code-block:: php - // app/config/security.php - use AppBundle\Entity\User; + // config/packages/security.php + use App\Entity\User; $container->loadFromExtension('security', array( 'providers' => array( @@ -82,26 +81,18 @@ a new provider that chains the two together: ), )); -Now, all firewalls that explicitly define ``chain_provider`` as their user -provider will, in turn, try to load the user from both the ``in_memory`` and -``user_db`` providers. - -.. versionadded:: 3.4 - In previous Symfony versions, firewalls that didn't define their user provider - explicitly, used the first existing provider (``chain_provider`` in this - example). However, auto-selecting the first user provider has been deprecated - in Symfony 3.4 and will throw an exception in 4.0. Always define the provider - used by the firewall when there are multiple providers. +Now, all firewalls that define ``chain_provider`` as their user provider will, +in turn, try to load the user from both the ``in_memory`` and ``user_db`` +providers. You can also configure the firewall or individual authentication mechanisms -to use a specific provider. Again, unless a provider is specified explicitly, -the first provider is always used: +to use a specific provider: .. configuration-block:: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: firewalls: secured_area: @@ -115,7 +106,7 @@ the first provider is always used: .. code-block:: xml - + loadFromExtension('security', array( 'firewalls' => array( 'secured_area' => array( @@ -151,10 +142,10 @@ the first provider is always used: ), )); -In this example, if a user tries to log in via HTTP authentication, the authentication -system will use the ``in_memory`` user provider. But if the user tries to -log in via the form login, the ``user_db`` provider will be used (since it's -the default for the firewall as a whole). +In this example, if a user tries to log in via HTTP authentication, the +authentication system will use the ``in_memory`` user provider. But if the user +tries to log in via the form login, the ``user_db`` provider will be used (since +it's the default for the firewall as a whole). For more information about user provider and firewall configuration, see the :doc:`/reference/configuration/security`. diff --git a/security/named_encoders.rst b/security/named_encoders.rst index 9b7d505b787..f9137037b94 100644 --- a/security/named_encoders.rst +++ b/security/named_encoders.rst @@ -11,7 +11,7 @@ to apply to all instances of a specific class: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... encoders: @@ -19,7 +19,7 @@ to apply to all instances of a specific class: .. code-block:: xml - + loadFromExtension('security', array( @@ -61,7 +61,7 @@ named encoders: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... encoders: @@ -71,7 +71,7 @@ named encoders: .. code-block:: xml - + loadFromExtension('security', array( // ... 'encoders' => array( @@ -139,16 +139,16 @@ you must register a service for it in order to use it as a named encoder: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... encoders: app_encoder: - id: 'app.password_encoder_service' + id: 'App\Security\Encoder\MyCustomPasswordEncoder' .. code-block:: xml - + + id="App\Security\Encoder\MyCustomPasswordEncoder" /> .. code-block:: php - // app/config/security.php + // config/packages/security.php + // ... + use App\Security\Encoder\MyCustomPasswordEncoder; + $container->loadFromExtension('security', array( // ... 'encoders' => array( 'app_encoder' => array( - 'id' => 'app.password_encoder_service', + 'id' => MyCustomPasswordEncoder::class, ), ), )); -This creates an encoder named ``app_encoder`` from a service named -``app.password_encoder_service``. +This creates an encoder named ``app_encoder`` from a service with the ID +``App\Security\Encoder\MyCustomPasswordEncoder``. .. _`libsodium`: https://pecl.php.net/package/libsodium diff --git a/security/password_encoding.rst b/security/password_encoding.rst index 0cad90600d7..2a3c6509eb8 100644 --- a/security/password_encoding.rst +++ b/security/password_encoding.rst @@ -17,10 +17,10 @@ in the following way from a controller:: use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; - public function registerAction(UserPasswordEncoderInterface $encoder) + public function register(UserPasswordEncoderInterface $encoder) { // whatever *your* User object is - $user = new AppBundle\Entity\User(); + $user = new App\Entity\User(); $plainPassword = 'ryanpass'; $encoded = $encoder->encodePassword($user, $plainPassword); @@ -28,8 +28,8 @@ in the following way from a controller:: } In order for this to work, just make sure that you have the encoder for your -user class (e.g. ``AppBundle\Entity\User``) configured under the ``encoders`` -key in ``app/config/security.yml``. +user class (e.g. ``App\Entity\User``) configured under the ``encoders`` +key in ``config/packages/security.yaml``. The ``$encoder`` object also has an ``isPasswordValid()`` method, which takes the ``User`` object as the first argument and the plain password to check diff --git a/security/pre_authenticated.rst b/security/pre_authenticated.rst index 966b657d52d..4063ddeaba0 100644 --- a/security/pre_authenticated.rst +++ b/security/pre_authenticated.rst @@ -32,7 +32,7 @@ Enable the x509 authentication for a particular firewall in the security configu .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -44,7 +44,7 @@ Enable the x509 authentication for a particular firewall in the security configu .. code-block:: xml - + loadFromExtension('security', array( // ... @@ -111,7 +111,7 @@ corresponding firewall in your security configuration: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: firewalls: secured_area: @@ -121,7 +121,7 @@ corresponding firewall in your security configuration: .. code-block:: xml - + @@ -135,7 +135,7 @@ corresponding firewall in your security configuration: .. code-block:: php - // app/config/security.php + // config/packages/security.php $container->loadFromExtension('security', array( 'firewalls' => array( 'secured_area' => array( diff --git a/security/remember_me.rst b/security/remember_me.rst index 8bbc6656647..4ad73df4c64 100644 --- a/security/remember_me.rst +++ b/security/remember_me.rst @@ -14,7 +14,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: # ... @@ -22,7 +22,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: main: # ... remember_me: - secret: '%secret%' + secret: '%kernel.secret%' lifetime: 604800 # 1 week in seconds path: / # by default, the feature is enabled by checking a @@ -32,7 +32,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: .. code-block:: xml - + +
    getMessage() ?>
    @@ -242,14 +242,14 @@ In the following example, the action is only allowed if the user has the use Symfony\Component\Security\Core\Exception\AccessDeniedException; // ... - public function editAction() + public function edit() { $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); // ... } -If your application is based on the Symfony Standard Edition, you can also secure +If you have installed `SensioFrameworkExtraBundle`_ in your application, you can also secure your controller using annotations:: use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; @@ -257,7 +257,7 @@ your controller using annotations:: /** * @Security("is_granted('IS_AUTHENTICATED_FULLY')") */ - public function editAction($name) + public function edit($name) { // ... } @@ -274,13 +274,15 @@ your controller using annotations:: * Once the user has entered their username and password, assuming the user receives the ``ROLE_USER`` role per your configuration, the user will have the ``IS_AUTHENTICATED_FULLY`` role and be able to access - any page in the account section, including the ``editAction()`` controller. + any page in the account section, including the ``edit()`` controller. * If the user's session ends, when the user returns to the site, they will be able to access every account page - except for the edit page - without being forced to re-authenticate. However, when they try to access the - ``editAction()`` controller, they will be forced to re-authenticate, since + ``edit()`` controller, they will be forced to re-authenticate, since they are not, yet, fully authenticated. For more information on securing services or methods in this way, see :doc:`/security/securing_services`. + +.. _`SensioFrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html diff --git a/security/securing_services.rst b/security/securing_services.rst index bb7075e89b3..3c2c07131f6 100644 --- a/security/securing_services.rst +++ b/security/securing_services.rst @@ -7,28 +7,31 @@ How to Secure any Service or Method in your Application In the security article, you can see how to :ref:`secure a controller ` by requesting -the ``security.authorization_checker`` service from the Service Container and -checking the current user's role:: +the ``Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface`` +service from the Service Container and checking the current user's role:: // ... + use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; - public function helloAction($name) + public function hello(AuthorizationCheckerInterface $authChecker) { - $this->denyAccessUnlessGranted('ROLE_ADMIN'); + if (!$authChecker->isGranted('ROLE_ADMIN')) { + throw new AccessDeniedException(); + } // ... } -You can also secure *any* service by injecting the ``security.authorization_checker`` +You can also secure *any* service by injecting the authorization checker service into it. For a general introduction to injecting dependencies into services see the :doc:`/service_container` article. For example, suppose you have a ``NewsletterManager`` class that sends out emails and you want to restrict its use to only users who have some ``ROLE_NEWSLETTER_ADMIN`` role. Before you add security, the class looks something like this:: - // src/AppBundle/Newsletter/NewsletterManager.php - namespace AppBundle\Newsletter; + // src/Newsletter/NewsletterManager.php + namespace App\Newsletter; class NewsletterManager { @@ -41,10 +44,11 @@ Before you add security, the class looks something like this:: } Your goal is to check the user's role when the ``sendNewsletter()`` method is -called. The first step towards this is to inject the ``security.authorization_checker`` +called. The first step towards this is to inject the +``Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface`` service into the object:: - // src/AppBundle/Newsletter/NewsletterManager.php + // src/Newsletter/NewsletterManager.php // ... use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; @@ -52,16 +56,16 @@ service into the object:: class NewsletterManager { - protected $authorizationChecker; + protected $authChecker; - public function __construct(AuthorizationCheckerInterface $authorizationChecker) + public function __construct(AuthorizationCheckerInterface $authChecker) { - $this->authorizationChecker = $authorizationChecker; + $this->authChecker = $authChecker; } public function sendNewsletter() { - if (!$this->authorizationChecker->isGranted('ROLE_NEWSLETTER_ADMIN')) { + if (!$this->authChecker->isGranted('ROLE_NEWSLETTER_ADMIN')) { throw new AccessDeniedException(); } @@ -71,21 +75,9 @@ service into the object:: // ... } -If you're using the :ref:`default services.yml configuration `, -Symfony will automatically pass the ``security.authorization_checker`` to your service +If you're using the :ref:`default services.yaml configuration `, +Symfony will automatically pass the authorization checker to your service thanks to autowiring and the ``AuthorizationCheckerInterface`` type-hint. If the current user does not have the ``ROLE_NEWSLETTER_ADMIN``, they will be prompted to log in. - -Securing Methods Using Annotations ----------------------------------- - -You can also secure method calls in any service with annotations by using the -optional `JMSSecurityExtraBundle`_ bundle. This bundle is not included in the -Symfony Standard Distribution, but you can choose to install it. - -See the `JMSSecurityExtraBundle Documentation`_ for more details. - -.. _`JMSSecurityExtraBundle`: https://github.com/schmittjoh/JMSSecurityExtraBundle -.. _`JMSSecurityExtraBundle Documentation`: http://jmsyst.com/bundles/JMSSecurityExtraBundle diff --git a/security/security_checker.rst b/security/security_checker.rst index a4018f90e69..904e34f0876 100644 --- a/security/security_checker.rst +++ b/security/security_checker.rst @@ -5,9 +5,17 @@ How to Check for Known Security Vulnerabilities in Your Dependencies ==================================================================== When using lots of dependencies in your Symfony projects, some of them may -contain security vulnerabilities. That's why Symfony includes a command called +contain security vulnerabilities. That's why Symfony provides a command called ``security:check`` that checks your ``composer.lock`` file to find any known -security vulnerability in your installed dependencies: +security vulnerability in your installed dependencies. + +First, install the security checker in your project: + +.. code-block:: terminal + + $ composer require security-checker + +Then run this command: .. code-block:: terminal @@ -24,11 +32,6 @@ FriendsOfPHP organization. any of your dependencies is affected by a known security vulnerability. Therefore, you can easily integrate it in your build process. -.. note:: - - To enable the ``security:check`` command, make sure the - `SensioDistributionBundle`_ is installed and enabled in your application. - .. tip:: The security checker is also available as an independent console application @@ -36,5 +39,4 @@ FriendsOfPHP organization. Check out the `Security Checker repository`_ for more details. .. _`security advisories database`: https://github.com/FriendsOfPHP/security-advisories -.. _`SensioDistributionBundle`: https://github.com/sensiolabs/SensioDistributionBundle .. _`Security Checker repository`: https://github.com/sensiolabs/security-checker diff --git a/security/user_checkers.rst b/security/user_checkers.rst index 85d9d2e3ebb..0695dcfd70f 100644 --- a/security/user_checkers.rst +++ b/security/user_checkers.rst @@ -20,10 +20,10 @@ are not met, an exception should be thrown which extends the .. code-block:: php - namespace AppBundle\Security; + namespace App\Security; - use AppBundle\Exception\AccountDeletedException; - use AppBundle\Security\User as AppUser; + use App\Exception\AccountDeletedException; + use App\Security\User as AppUser; use Symfony\Component\Security\Core\Exception\AccountExpiredException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -59,7 +59,7 @@ Enabling the Custom User Checker -------------------------------- Next, make sure your user checker is registered as a service. If you're using the -:ref:`default services.yml configuration `, +:ref:`default services.yaml configuration `, the service is registered automatically. All that's left to do is add the checker to the desired firewall where the value @@ -69,19 +69,19 @@ is the service id of your user checker: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml # ... security: firewalls: main: pattern: ^/ - user_checker: AppBundle\Security\UserChecker + user_checker: App\Security\UserChecker # ... .. code-block:: xml - + - AppBundle\Security\UserChecker + App\Security\UserChecker @@ -100,10 +100,10 @@ is the service id of your user checker: .. code-block:: php - // app/config/security.php + // config/packages/security.php // ... - use AppBundle\Security\UserChecker; + use App\Security\UserChecker; $container->loadFromExtension('security', array( 'firewalls' => array( diff --git a/security/voters.rst b/security/voters.rst index bec6d9d12b3..ec817f5b6c2 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -18,7 +18,7 @@ How Symfony Uses Voters In order to use voters, you have to understand how Symfony works with them. All voters are called each time you use the ``isGranted()`` method on Symfony's -authorization checker or call ``denyAccessUnlessGranted`` in a controller (which +authorization checker or call ``denyAccessUnlessGranted()`` in a controller (which uses the authorization checker). Ultimately, Symfony takes the responses from all voters and makes the final @@ -51,7 +51,7 @@ Suppose you have a ``Post`` object and you need to decide whether or not the cur user can *edit* or *view* the object. In your controller, you'll check access with code like this:: - // src/AppBundle/Controller/PostController.php + // src/Controller/PostController.php // ... class PostController extends Controller @@ -59,7 +59,7 @@ code like this:: /** * @Route("/posts/{id}", name="post_show") */ - public function showAction($id) + public function show($id) { // get a Post object - e.g. query for it $post = ...; @@ -73,7 +73,7 @@ code like this:: /** * @Route("/posts/{id}/edit", name="post_edit") */ - public function editAction($id) + public function edit($id) { // get a Post object - e.g. query for it $post = ...; @@ -98,11 +98,11 @@ pretty complex. For example, a ``User`` can always edit or view a ``Post`` they And if a ``Post`` is marked as "public", anyone can view it. A voter for this situation would look like this:: - // src/AppBundle/Security/PostVoter.php - namespace AppBundle\Security; + // src/Security/PostVoter.php + namespace App\Security; - use AppBundle\Entity\Post; - use AppBundle\Entity\User; + use App\Entity\Post; + use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; @@ -197,7 +197,7 @@ Configuring the Voter To inject the voter into the security layer, you must declare it as a service and tag it with ``security.voter``. But if you're using the -:ref:`default services.yml configuration `, +:ref:`default services.yaml configuration `, that's done automatically for you! When you :ref:`call isGranted() with view/edit and pass a Post object `, your voter will be executed and you can control access. @@ -211,7 +211,7 @@ the :class:`Symfony\\Component\\Security\\Core\\Authorization\\AccessDecisionMan into your voter. You can use this to, for example, *always* allow access to a user with ``ROLE_SUPER_ADMIN``:: - // src/AppBundle/Security/PostVoter.php + // src/Security/PostVoter.php // ... use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; @@ -240,7 +240,7 @@ with ``ROLE_SUPER_ADMIN``:: } } -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, you're done! Symfony will automatically pass the ``security.access.decision_manager`` service when instantiating your voter (thanks to autowiring). @@ -289,7 +289,7 @@ security configuration: .. code-block:: yaml - # app/config/security.yml + # config/packages/security.yaml security: access_decision_manager: strategy: unanimous @@ -297,7 +297,7 @@ security configuration: .. code-block:: xml - + loadFromExtension('security', array( 'access_decision_manager' => array( 'strategy' => 'unanimous', diff --git a/serializer.rst b/serializer.rst index 571e45338cc..7430479565f 100644 --- a/serializer.rst +++ b/serializer.rst @@ -4,72 +4,31 @@ How to Use the Serializer ========================= -Serializing and deserializing to and from objects and different formats (e.g. -JSON or XML) is a very complex topic. Symfony comes with a -:doc:`Serializer Component `, which gives you some -tools that you can leverage for your solution. +Symfony provides a serializer to serialize/deserialize to and from objects and +different formats (e.g. JSON or XML). Before using it, read the +:doc:`Serializer component docs ` to get familiar with +its philosophy and the normalizers and encoders terminology. -In fact, before you start, get familiar with the serializer, normalizers -and encoders by reading the :doc:`Serializer Component `. +.. _activating_the_serializer: -Activating the Serializer -------------------------- - -The ``serializer`` service is not available by default. To turn it on, activate -it in your configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - # ... - serializer: { enable_annotations: true } - # Alternatively, if you don't want to use annotations - #serializer: { enabled: true } +Installation +------------ - .. code-block:: xml +In applications using :doc:`Symfony Flex `, run this command to +install the serializer before using it: - - - - - - - - - +.. code-block:: terminal - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - // ... - 'serializer' => array( - 'enable_annotations' => true, - // Alternatively, if you don't want to use annotations - //'enabled' => true, - ), - )); + $ composer require serializer Using the Serializer Service ---------------------------- -Once enabled, the ``serializer`` service can be injected in any service where +Once enabled, the serializer service can be injected in any service where you need it or it can be used in a controller:: - // src/AppBundle/Controller/DefaultController.php - namespace AppBundle\Controller; + // src/Controller/DefaultController.php + namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Serializer\SerializerInterface; @@ -122,7 +81,7 @@ properties and setters (``setXxx()``) to change properties: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: get_set_method_normalizer: class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer @@ -131,7 +90,7 @@ properties and setters (``setXxx()``) to change properties: .. code-block:: xml - + register('get_set_method_normalizer', GetSetMethodNormalizer::class) @@ -155,56 +114,16 @@ properties and setters (``setXxx()``) to change properties: ->addTag('serializer.normalizer') ; -.. versionadded:: 3.4 - Support for hasser methods (``hasXxx()``) in ``GetSetMethodNormalizer`` was - introduced in Symfony 3.4. In previous Symfony versions only getters (``getXxx()``) - and issers (``isXxx()``) were supported. - .. _serializer-using-serialization-groups-annotations: Using Serialization Groups Annotations -------------------------------------- -Enable :ref:`serialization groups annotation ` -with the following configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - # ... - serializer: - enable_annotations: true - - .. code-block:: xml - - - - - - - - - - +To use annotations, first install the annotations package: - .. code-block:: php +.. code-block:: terminal - // app/config/config.php - $container->loadFromExtension('framework', array( - // ... - 'serializer' => array( - 'enable_annotations' => true, - ), - )); + $ composer require annotations Next, add the :ref:`@Groups annotations ` to your class and choose which groups to use when serializing:: @@ -215,62 +134,21 @@ to your class and choose which groups to use when serializing:: ); In addition to the ``@Groups`` annotation, the Serializer component also -supports Yaml or XML files. These files are automatically loaded when being +supports YAML or XML files. These files are automatically loaded when being stored in one of the following locations: -* The ``serialization.yml`` or ``serialization.xml`` file in +* The ``serialization.yaml`` or ``serialization.xml`` file in the ``Resources/config/`` directory of a bundle; -* All ``*.yml`` and ``*.xml`` files in the ``Resources/config/serialization/`` +* All ``*.yaml`` and ``*.xml`` files in the ``Resources/config/serialization/`` directory of a bundle. .. _serializer-enabling-metadata-cache: -Enabling the Metadata Cache ---------------------------- - -Metadata used by the Serializer component such as groups can be cached to -enhance application performance. Any service implementing the ``Doctrine\Common\Cache\Cache`` -interface can be used. - -A service leveraging `APCu`_ (and APC for PHP < 5.5) is built-in. - -.. configuration-block:: +Configuring the Metadata Cache +------------------------------ - .. code-block:: yaml - - # app/config/config_prod.yml - framework: - # ... - serializer: - cache: serializer.mapping.cache.apc - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/config_prod.php - $container->loadFromExtension('framework', array( - // ... - 'serializer' => array( - 'cache' => 'serializer.mapping.cache.apc', - ), - )); +The metadata for the serializer is automatically cached. To configure the cache, +configure the ``framework.cache.pools`` key in ``config/packages/framework.yaml``. Enabling a Name Converter ------------------------- @@ -287,7 +165,7 @@ value: .. code-block:: yaml - # app/config/config.yml + # config/packages/framework.yaml framework: # ... serializer: @@ -295,7 +173,7 @@ value: .. code-block:: xml - + @@ -303,7 +181,7 @@ value: .. code-block:: php - // app/config/config.php + // config/packages/framework.php $container->loadFromExtension('framework', array( // ... 'serializer' => array( @@ -324,9 +202,8 @@ take a look at how this bundle works. .. toctree:: :maxdepth: 1 - :glob: - serializer/* + serializer/custom_encoders .. _`APCu`: https://github.com/krakjoe/apcu .. _`ApiPlatform`: https://github.com/api-platform/core diff --git a/serializer/custom_encoders.rst b/serializer/custom_encoders.rst index 9403ee7f265..4809cfe69bf 100644 --- a/serializer/custom_encoders.rst +++ b/serializer/custom_encoders.rst @@ -9,17 +9,17 @@ to transform any data to an array. Then, by leveraging *Encoders*, that data can be converted into any data-structure (e.g. JSON). The Component provides several built-in encoders that are described -:doc:`in their own section ` but you may want +:doc:`in the serializer component ` but you may want to use another structure that's not supported. Creating a new encoder ---------------------- -Imagine you want to serialize and deserialize Yaml. For that you'll have to +Imagine you want to serialize and deserialize YAML. For that you'll have to create your own encoder that uses the :doc:`Yaml Component `:: - namespace AppBundle\Serializer; + namespace App\Serializer; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; @@ -52,14 +52,14 @@ Registering it in your app -------------------------- If you use the Symfony Framework. then you probably want to register this encoder -as a service in your app. If you're using the :ref:`default services.yml configuration `, +as a service in your app. If you're using the :ref:`default services.yaml configuration `, that's done automatically! .. tip:: - If you're not using autoconfigure, make sure to register your class as a service - and tag it with ``serializer.encoder``. + If you're not using :ref:`autoconfigure `, make sure + to register your class as a service and tag it with ``serializer.encoder``. -Now you'll be able to serialize and deserialize Yaml! +Now you'll be able to serialize and deserialize YAML! .. _tracker: https://github.com/symfony/symfony/issues diff --git a/serializer/encoders.rst b/serializer/encoders.rst deleted file mode 100644 index f205efec5c0..00000000000 --- a/serializer/encoders.rst +++ /dev/null @@ -1,76 +0,0 @@ -.. index:: - single: Serializer, Encoders - -Encoders -======== - -Encoders basically turn **arrays** into **formats** and vice versa. -They implement -:class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` for -encoding (array to format) and -:class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface` for -decoding (format to array). - -You can add new encoders to a Serializer instance by using its second constructor argument:: - - use Symfony\Component\Serializer\Serializer; - use Symfony\Component\Serializer\Encoder\XmlEncoder; - use Symfony\Component\Serializer\Encoder\JsonEncoder; - - $encoders = array(new XmlEncoder(), new JsonEncoder()); - $serializer = new Serializer(array(), $encoders); - -Built-in Encoders ------------------ - -The Serializer component provides built-in encoders: - -* :class:`Symfony\\Component\\Serializer\\Encoder\\CsvEncoder` to encode/decode CSV -* :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` to encode/decode JSON -* :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder` to encode/decode XML -* :class:`Symfony\\Component\\Serializer\\Encoder\\YamlEncoder` to encode/decode Yaml - -.. versionadded:: 3.2 - The :class:`Symfony\\Component\\Serializer\\Encoder\\CsvEncoder` and the - :class:`Symfony\\Component\\Serializer\\Encoder\\YamlEncoder` were introduced in - Symfony 3.2. - -The ``JsonEncoder`` -~~~~~~~~~~~~~~~~~~~ - -The ``JsonEncoder`` encodes to and decodes from JSON strings, based on the PHP -:phpfunction:`json_encode` and :phpfunction:`json_decode` functions. - -The ``XmlEncoder`` -~~~~~~~~~~~~~~~~~~ - -This encoder transforms arrays into XML and vice versa. - -For example, take an object normalized as following:: - - array('foo' => array(1, 2), 'bar' => true); - -The ``XmlEncoder`` will encode this object like that:: - - - - 1 - 2 - 1 - - -Be aware that this encoder will consider keys beginning with ``@`` as attributes:: - - $encoder = new XmlEncoder(); - $encoder->encode(array('foo' => array('@bar' => 'value'))); - // will return: - // - // - // - // - -The ``YamlEncoder`` -~~~~~~~~~~~~~~~~~~~ - -This encoder requires the :doc:`Yaml Component ` and -transforms from and to Yaml. diff --git a/service_container.rst b/service_container.rst index 774b1e3daa1..c52c29a3d3d 100644 --- a/service_container.rst +++ b/service_container.rst @@ -10,15 +10,10 @@ send emails while another object might help you save things to the database. Almost *everything* that your app "does" is actually done by one of these objects. And each time you install a new bundle, you get access to even more! -In Symfony, these useful objects are called **services** and each service lives inside -a very special object called the **service container**. If you have the service container, -then you can fetch a service by using that service's id:: - - $logger = $container->get('logger'); - $entityManager = $container->get('doctrine.orm.entity_manager'); - -The container allows you to centralize the way objects are constructed. It makes -your life easier, promotes a strong architecture and is super fast! +In Symfony, these useful objects are called **services** and each service lives +inside a very special object called the **service container**. The container +allows you to centralize the way objects are constructed. It makes your life +easier, promotes a strong architecture and is super fast! Fetching and using Services --------------------------- @@ -28,7 +23,7 @@ These are like *tools*: waiting for you to take advantage of them. In your contr you can "ask" for a service from the container by type-hinting an argument with the service's class or interface name. Want to :doc:`log ` something? No problem:: - // src/AppBundle/Controller/ProductController.php + // src/Controller/ProductController.php // ... use Psr\Log\LoggerInterface; @@ -36,79 +31,44 @@ service's class or interface name. Want to :doc:`log ` something? No p /** * @Route("/products") */ - public function listAction(LoggerInterface $logger) + public function list(LoggerInterface $logger) { $logger->info('Look! I just used a service'); // ... } -.. versionadded:: 3.3 - The ability to type-hint a service in order to receive it was added in Symfony 3.3. - See the :ref:`controller chapter ` for more - details. - -.. _container-debug-container: - What other services are available? Find out by running: .. code-block:: terminal - $ php bin/console debug:container - -This is just a *small* sample of the output: + $ php bin/console debug:autowiring -=============================== ======================================================================= -Service ID Class name -=============================== ======================================================================= -doctrine ``Doctrine\Bundle\DoctrineBundle\Registry`` -filesystem ``Symfony\Component\Filesystem\Filesystem`` -form.factory ``Symfony\Component\Form\FormFactory`` -logger ``Symfony\Bridge\Monolog\Logger`` -request_stack ``Symfony\Component\HttpFoundation\RequestStack`` -router ``Symfony\Bundle\FrameworkBundle\Routing\Router`` -security.authorization_checker ``Symfony\Component\Security\Core\Authorization\AuthorizationChecker`` -security.password_encoder ``Symfony\Component\Security\Core\Encoder\UserPasswordEncoder`` -session ``Symfony\Component\HttpFoundation\Session\Session`` -translator ``Symfony\Component\Translation\DataCollectorTranslator`` -twig ``Twig_Environment`` -validator ``Symfony\Component\Validator\Validator\ValidatorInterface`` -=============================== ======================================================================= - -You can also use the unique "Service ID" to access a service directly:: - - // src/AppBundle/Controller/ProductController.php - namespace AppBundle\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Symfony\Component\Routing\Annotation\Route; - - class ProductController extends Controller - { - /** - * @Route("/products") - */ - public function listAction() - { - $logger = $this->container->get('logger'); - $logger->info('Look! I just used a service'); - - // ... - } - } +=============================================================== ===================================== +Class/Interface Type Alias Service ID +=============================================================== ===================================== +``Psr\Cache\CacheItemPoolInterface`` alias for "cache.app.recorder" +``Psr\Log\LoggerInterface`` alias for "monolog.logger" +``Symfony\Component\EventDispatcher\EventDispatcherInterface`` alias for "debug.event_dispatcher" +``Symfony\Component\HttpFoundation\RequestStack`` alias for "request_stack" +``Symfony\Component\HttpFoundation\Session\SessionInterface`` alias for "session" +``Symfony\Component\Routing\RouterInterface`` alias for "router.default" +=============================================================== ===================================== -:ref:`Fetching a service directly from the container ` -like this only works if you extend the ``Controller`` class. +When you use these type-hints in your controller methods or inside your +:ref:`own services `, Symfony will automatically +pass you the service object matching that type. Throughout the docs, you'll see how to use the many different services that live in the container. -.. sidebar:: Container: Lazy-loaded for speed +.. tip:: - Wait! Are all the services (objects) instantiated on *every* request? No! The - container is lazy: it doesn't instantiate a service until (and unless) you ask - for it. For example, if you never use the ``validator`` service during a request, - the container will never instantiate it. + There are actually *many* more services in the container, and each service has + a unique id in the container, like ``session`` or ``router.default``. For a full + list, you can run ``php bin/console debug:container``. But most of the time, + you won't need to worry about this. See :ref:`services-wire-specific-service`. + See :doc:`/service_container/debug`. .. index:: single: Service Container; Configuring services @@ -118,17 +78,12 @@ in the container. Creating/Configuring Services in the Container ---------------------------------------------- -.. tip:: - - The recommended way of configuring services changed in Symfony 3.3. For a deep - explanation, see :doc:`/service_container/3.3-di-changes`. - You can also organize your *own* code into services. For example, suppose you need to show your users a random, happy message. If you put this code in your controller, it can't be re-used. Instead, you decide to create a new class:: - // src/AppBundle/Service/MessageGenerator.php - namespace AppBundle\Service; + // src/Service/MessageGenerator.php + namespace App\Service; class MessageGenerator { @@ -149,9 +104,9 @@ it can't be re-used. Instead, you decide to create a new class:: Congratulations! You've just created your first service class! You can use it immediately inside your controller:: - use AppBundle\Service\MessageGenerator; + use App\Service\MessageGenerator; - public function newAction(MessageGenerator $messageGenerator) + public function new(MessageGenerator $messageGenerator) { // thanks to the type-hint, the container will instantiate a // new MessageGenerator and pass it to you! @@ -170,34 +125,36 @@ each time you ask for it. .. _service-container-services-load-example: -.. sidebar:: Automatic Service Loading in services.yml +.. sidebar:: Automatic Service Loading in services.yaml - The documentation assumes you're using - `Symfony Standard Edition (version 3.3) services.yml`_ configuration. The most - important part is this: + The documentation assumes you're using the following service configuration, + which is the default config for a new project: .. configuration-block:: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # default configuration for services in *this* file _defaults: - autowire: true - autoconfigure: true - public: false + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + public: false # Allows optimizing the container by removing unused services; this also means + # fetching services directly from the container via $container->get() won't work. + # The best practice is to be explicit about your dependencies anyway. + + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\: + resource: '../src/*' + exclude: '../src/{Entity,Migrations,Tests,Kernel.php}' - # makes classes in src/AppBundle available to be used as services - AppBundle\: - resource: '../../src/AppBundle/*' - # you can exclude directories or files - # but if a service is unused, it's removed anyway - exclude: '../../src/AppBundle/{Entity,Repository}' + # ... .. code-block:: xml - + - - + .. code-block:: php - // app/config/services.php + // config/services.php use Symfony\Component\DependencyInjection\Definition; // To use as default template @@ -228,7 +184,7 @@ each time you ask for it. ; // $this is a reference to the current loader - $this->registerClasses($definition, 'AppBundle\\', '../../src/AppBundle/*', '../../src/AppBundle/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Migrations,Tests}'); .. tip:: @@ -236,57 +192,23 @@ each time you ask for it. `glob pattern`_. Thanks to this configuration, you can automatically use any classes from the - ``src/AppBundle`` directory as a service, without needing to manually configure + ``src/`` directory as a service, without needing to manually configure it. Later, you'll learn more about this in :ref:`service-psr4-loader`. If you'd prefer to manually wire your service, that's totally possible: see :ref:`services-explicitly-configure-wire-services`. - .. versionadded:: 3.3 - The ``_defaults`` key *and* ability to load services from a directory were added - in Symfony 3.3. - -You can also fetch a service directly from the container via its "id", which will -be its class name in this case:: - - use AppBundle\Service\MessageGenerator; - - // accessing services like this only works if you extend Controller - class ProductController extends Controller - { - public function newAction() - { - // only works if your service is public - $messageGenerator = $this->get(MessageGenerator::class); - - $message = $messageGenerator->getHappyMessage(); - $this->addFlash('success', $message); - // ... - } - } - -However, this only works if you make your service :ref:`public `. - -.. caution:: - - Service ids are case-insensitive (e.g. ``AppBundle\Service\MessageGenerator`` - and ``appbundle\service\messagegenerator`` refer to the same service). But this - was deprecated in Symfony 3.3. Starting in 4.0, service ids will be case sensitive. - .. _services-constructor-injection: Injecting Services/Config into a Service ---------------------------------------- What if you need to access the ``logger`` service from within ``MessageGenerator``? -Your service does *not* have access to the container directly, so you can't fetch -it via ``$this->container->get()``. - -No problem! Instead, create a ``__construct()`` method with a ``$logger`` argument -that has the ``LoggerInterface`` type-hint. Set this on a new ``$logger`` property +No problem! Create a ``__construct()`` method with a ``$logger`` argument that has +the ``LoggerInterface`` type-hint. Set this on a new ``$logger`` property and use it later:: - // src/AppBundle/Service/MessageGenerator.php + // src/Service/MessageGenerator.php // ... use Psr\Log\LoggerInterface; @@ -311,10 +233,13 @@ That's it! The container will *automatically* know to pass the ``logger`` servic when instantiating the ``MessageGenerator``. How does it know to do this? :ref:`Autowiring `. The key is the ``LoggerInterface`` type-hint in your ``__construct()`` method and the ``autowire: true`` config in -``services.yml``. When you type-hint an argument, the container will automatically +``services.yaml``. When you type-hint an argument, the container will automatically find the matching service. If it can't, you'll see a clear exception with a helpful suggestion. +By the way, this method of adding dependencies to your ``__construct()`` method is +called *dependency injection*. It's a scary term for a simple concept. + .. _services-debug-container-types: How should you know to use ``LoggerInterface`` for the type-hint? You can either @@ -325,10 +250,10 @@ type-hints by running: $ php bin/console debug:autowiring -This is just a small subset of the output: +This command is your best friend. This is a small subset of the output: =============================================================== ===================================== -Service ID Class name +Class/Interface Type Alias Service ID =============================================================== ===================================== ``Psr\Cache\CacheItemPoolInterface`` alias for "cache.app.recorder" ``Psr\Log\LoggerInterface`` alias for "monolog.logger" @@ -344,10 +269,10 @@ Handling Multiple Services Suppose you also want to email a site administrator each time a site update is made. To do that, you create a new class:: - // src/AppBundle/Updates/SiteUpdateManager.php - namespace AppBundle\Updates; + // src/Updates/SiteUpdateManager.php + namespace App\Updates; - use AppBundle\Service\MessageGenerator; + use App\Service\MessageGenerator; class SiteUpdateManager { @@ -375,13 +300,16 @@ made. To do that, you create a new class:: } } -This uses the ``MessageGenerator`` *and* the ``Swift_Mailer`` service. As long as -you're :ref:`loading all services from src/AppBundle `, -you can use the service immediately:: +This needs the ``MessageGenerator`` *and* the ``Swift_Mailer`` service. That's no +problem! In fact, this new service is ready to be used. In a controller, for example, +you can type-hint the new ``SiteUpdateManager`` class and use it:: - use AppBundle\Updates\SiteUpdateManager; + // src/Controller/SiteController.php - public function newAction(SiteUpdateManager $siteUpdateManager) + // ... + use App\Updates\SiteUpdateManager; + + public function new(SiteUpdateManager $siteUpdateManager) { // ... @@ -406,7 +334,7 @@ example, suppose you want to make the admin email configurable: .. code-block:: diff - // src/AppBundle/Updates/SiteUpdateManager.php + // src/Updates/SiteUpdateManager.php // ... class SiteUpdateManager @@ -437,7 +365,7 @@ example, suppose you want to make the admin email configurable: If you make this change and refresh, you'll see an error: - Cannot autowire service "AppBundle\Updates\SiteUpdateManager": argument "$adminEmail" + Cannot autowire service "App\Updates\SiteUpdateManager": argument "$adminEmail" of method "__construct()" must have a type-hint or be given a value explicitly. That makes sense! There is no way that the container knows what value you want to @@ -447,23 +375,23 @@ pass here. No problem! In your configuration, you can explicitly set this argume .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... # same as before - AppBundle\: - resource: '../../src/AppBundle/*' - exclude: '../../src/AppBundle/{Entity,Repository}' + App\: + resource: '../src/*' + exclude: '../src/{Entity,Migrations,Tests}' # explicitly configure the service - AppBundle\Updates\SiteUpdateManager: + App\Updates\SiteUpdateManager: arguments: $adminEmail: 'manager@example.com' .. code-block:: xml - + - + - + manager@example.com @@ -485,8 +413,8 @@ pass here. No problem! In your configuration, you can explicitly set this argume .. code-block:: php - // app/config/services.php - use AppBundle\Updates\SiteUpdateManager; + // config/services.php + use App\Updates\SiteUpdateManager; use Symfony\Component\DependencyInjection\Definition; // Same as before @@ -498,20 +426,15 @@ pass here. No problem! In your configuration, you can explicitly set this argume ->setPublic(false) ; - $this->registerClasses($definition, 'AppBundle\\', '../../src/AppBundle/*', '../../src/AppBundle/{Entity,Repository}'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Migrations,Tests}'); // Explicitly configure the service $container->getDefinition(SiteUpdateManager::class) ->setArgument('$adminEmail', 'manager@example.com'); -.. versionadded:: 3.3 - The ability to configure an argument by its name (``$adminEmail``) was added - in Symfony 3.3. Previously, you could configure it only by its index (``2`` in - this case) or by using empty quotes for the other arguments. - -Thanks to this, the container will pass ``manager@example.com`` as the third argument -to ``__construct`` when creating the ``SiteUpdateManager`` service. The other arguments -will still be autowired. +Thanks to this, the container will pass ``manager@example.com`` to the ``$adminEmail`` +argument of ``__construct`` when creating the ``SiteUpdateManager`` service. The +other arguments will still be autowired. But, isn't this fragile? Fortunately, no! If you rename the ``$adminEmail`` argument to something else - e.g. ``$mainEmail`` - you will get a clear exception when you @@ -530,20 +453,20 @@ and reference it with the ``%parameter_name%`` syntax: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml parameters: admin_email: manager@example.com services: # ... - AppBundle\Updates\SiteUpdateManager: + App\Updates\SiteUpdateManager: arguments: $adminEmail: '%admin_email%' .. code-block:: xml - + - + %admin_email% @@ -565,17 +488,17 @@ and reference it with the ``%parameter_name%`` syntax: .. code-block:: php - // app/config/services.php - use AppBundle\Updates\SiteUpdateManager; + // config/services.php + use App\Updates\SiteUpdateManager; $container->setParameter('admin_email', 'manager@example.com'); $container->autowire(SiteUpdateManager::class) // ... ->setArgument('$adminEmail', '%admin_email%'); -Actually, once you define a parameter, it can be referenced via the ``%parameter_name%`` -syntax in *any* other service configuration file - like ``config.yml``. Many parameters -are defined in a :ref:`parameters.yml file `. +Actually, once you define a parameter, it can be referenced via the +``%parameter_name%`` syntax in *any* other configuration file. Many parameters +are defined in the ``config/services.yaml`` file. You can then fetch the parameter in the service:: @@ -593,11 +516,11 @@ You can then fetch the parameter in the service:: You can also fetch parameters directly from the container:: - public function newAction() + public function new() { // ... - // this ONLY works if you extend Controller + // this ONLY works if you extend the base Controller $adminEmail = $this->container->getParameter('admin_email'); // or a shorter way! @@ -613,7 +536,7 @@ Choose a Specific Service The ``MessageGenerator`` service created earlier requires a ``LoggerInterface`` argument:: - // src/AppBundle/Service/MessageGenerator.php + // src/Service/MessageGenerator.php // ... use Psr\Log\LoggerInterface; @@ -641,18 +564,18 @@ But, you can control this and pass in a different logger: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... same code as before # explicitly configure the service - AppBundle\Service\MessageGenerator: + App\Service\MessageGenerator: arguments: $logger: '@monolog.logger.request' .. code-block:: xml - + - + @@ -671,8 +594,8 @@ But, you can control this and pass in a different logger: .. code-block:: php - // app/config/services.php - use AppBundle\Service\MessageGenerator; + // config/services.php + use App\Service\MessageGenerator; use Symfony\Component\DependencyInjection\Reference; $container->autowire(MessageGenerator::class) @@ -683,6 +606,14 @@ But, you can control this and pass in a different logger: This tells the container that the ``$logger`` argument to ``__construct`` should use service whose id is ``monolog.logger.request``. +.. _container-debug-container: + +For a full list of *all* possible services in the container, run: + +.. code-block:: terminal + + php bin/console debug:container --show-private + .. tip:: The ``@`` symbol is important: that's what tells the container you want to pass @@ -694,51 +625,29 @@ service whose id is ``monolog.logger.request``. Binding Arguments by Name or Type --------------------------------- -You can also use the ``bind`` keyword to bind specific arguments by name or type. - -.. _services-autowire: - -The autowire Option -------------------- - -Above, the ``services.yml`` file has ``autowire: true`` in the ``_defaults`` section -so that it applies to all services defined in that file. With this setting, you're -able to type-hint arguments in the ``__construct()`` method of your services and -the container will automatically pass you the correct arguments. This entire entry -has been written around autowiring. - -For more details about autowiring, check out :doc:`/service_container/autowiring`. - -.. _services-autoconfigure: - -The autoconfigure Option ------------------------- - -.. versionadded:: 3.3 - The ``autoconfigure`` option was added in Symfony 3.3. - -Above, the ``services.yml`` file has ``autoconfigure: true`` in the ``_defaults`` -section so that it applies to all services defined in that file. With this setting, -the container will automatically apply certain configuration to your services, based -on your service's *class*. This is mostly used to *auto-tag* your services. - -For example, to create a Twig Extension, you need to create a class, register it -as a service, and :doc:`tag ` it with ``twig.extension``: +You can also use the ``bind`` keyword to bind specific arguments by name or type: .. configuration-block:: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - # ... + _defaults: + bind: + # pass this value to any $adminEmail argument for any service + # that's defined in this file (including controller arguments) + $adminEmail: 'manager@example.com' - AppBundle\Twig\MyTwigExtension: - tags: [twig.extension] + # pass this service for any LoggerInterface type-hint for any + # service that's defined in this file + Psr\Log\LoggerInterface: '@monolog.logger.request' + + # ... .. code-block:: xml - + ` it with ``twig.extension` http://symfony.com/schema/dic/services/services-1.0.xsd"> - + + manager@example.com + + - - - + .. code-block:: php - // app/config/services.php - use AppBundle\Twig\MyTwigExtension; + // config/services.php + use App\Controller\LuckyController; + use Symfony\Component\DependencyInjection\Reference; + use Psr\Log\LoggerInterface; + + $container->register(LuckyController::class) + ->setPublic(true) + ->setBindings(array( + '$adminEmail' => 'manager@example.com', + LoggerInterface::class => new Reference('monolog.logger.request'), + )) + ; + +By putting the ``bind`` key under ``_defaults``, you can specify the value of *any* +argument for *any* service defined in this file! You can bind arguments by name +(e.g. ``$adminEmail``) or by type (e.g. ``Psr\Log\LoggerInterface``). + +The ``bind`` config can be also be applied to specific services or when loading many +services at once (i.e. :ref:`service-psr4-loader`). + +Getting Container Parameters as a Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 4.1 + The feature to get container parameters as a service was introduced in Symfony 4.1. + +If some service or controller needs lots of container parameters, there's an +easier alternative to binding all of them with the ``services._defaults.bind`` +option. Type-hint any of its constructor arguments with the +:class:`Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBagInterface` +or the new :class:`Symfony\\Component\\DependencyInjection\\ParameterBag\\ContainerBagInterface` +and the service will get all container parameters in a +:class:`Symfony\\Component\\DependencyInjection\\ParameterBag\\ParameterBag` object:: + + // src/Service/MessageGenerator.php + // ... + + use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; + + class MessageGenerator + { + private $params; + + public function __construct(ParameterBagInterface $params) + { + $this->params = $params; + } + + public function someMethod() + { + // get any param from $this->params, which stores all container parameters + $sender = $this->params->get('mailer_sender'); + // ... + } + } + +.. _services-autowire: + +The autowire Option +------------------- + +Above, the ``services.yaml`` file has ``autowire: true`` in the ``_defaults`` section +so that it applies to all services defined in that file. With this setting, you're +able to type-hint arguments in the ``__construct()`` method of your services and +the container will automatically pass you the correct arguments. This entire entry +has been written around autowiring. + +For more details about autowiring, check out :doc:`/service_container/autowiring`. + +.. _services-autoconfigure: + +The autoconfigure Option +------------------------ + +Above, the ``services.yaml`` file has ``autoconfigure: true`` in the ``_defaults`` +section so that it applies to all services defined in that file. With this setting, +the container will automatically apply certain configuration to your services, based +on your service's *class*. This is mostly used to *auto-tag* your services. - $container->autowire(MyTwigExtension::class) - ->addTag('twig.extension'); +For example, to create a Twig extension, you need to create a class, register it +as a service, and :doc:`tag ` it with ``twig.extension``. But, with ``autoconfigure: true``, you don't need the tag. In fact, if you're using -the :ref:`Symfony Standard Edition services.yml config `, +the :ref:`default services.yaml config `, you don't need to do *anything*: the service will be automatically loaded. Then, ``autoconfigure`` will add the ``twig.extension`` tag *for* you, because your class implements ``Twig_ExtensionInterface``. And thanks to ``autowire``, you can even add constructor arguments without any configuration. -Of course, you can still :ref:`manually configure the service ` -if you need to. - .. _container-public: Public Versus Private Services ------------------------------ -Thanks to the ``_defaults`` section in ``services.yml``, every service defined in -this file is ``public: false`` by default: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/services.yml - services: - # default configuration for services in *this* file - _defaults: - # ... - public: false - - .. code-block:: xml - - - - - - - - - - +Thanks to the ``_defaults`` section in ``services.yaml``, every service defined in +this file is ``public: false`` by default. -What does this mean? When a service is **not** public, you cannot access it directly -from the container:: +What does this mean? When a service **is** public, you can access it directly +from the container object, which is accessible from any controller that extends +``Controller``:: - use AppBundle\Service\MessageGenerator; + use App\Service\MessageGenerator; - public function newAction(MessageGenerator $messageGenerator) + // ... + public function new() { - // type-hinting it as an argument DOES work + // there IS a public "logger" service in the container + $logger = $this->container->get('logger'); - // but accessing it directly from the container does NOT Work - $this->container->get(MessageGenerator::class); + // this will NOT work: MessageGenerator is a private service + $generator = $this->container->get(MessageGenerator::class); } -Usually, this is ok: there are better ways to access a service. But, if you *do* -need to make your service public, just override this setting: +As a best practice, you should only create *private* services, which will happen +automatically. And also, you should *not* use the ``$container->get()`` method to +fetch public services. + +But, if you *do* need to make a service public, just override the ``public`` setting: .. configuration-block:: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... same code as before # explicitly configure the service - AppBundle\Service\MessageGenerator: + App\Service\MessageGenerator: public: true .. code-block:: xml - + - + @@ -863,27 +829,19 @@ key. For example, the default Symfony configuration contains this: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - # the namespace prefix for classes (must end in \) - AppBundle\: - # create services for all the classes found in this directory... - resource: '../../src/AppBundle/*' - # ...except for the classes located in these directories - exclude: '../../src/AppBundle/{Entity,Repository}' - - # these were imported above, but we want to add some extra config - AppBundle\Controller\: - resource: '../../src/AppBundle/Controller' - # apply some configuration to these services - public: true - tags: ['controller.service_arguments'] + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\: + resource: '../src/*' + exclude: '../src/{Entity,Migrations,Tests}' .. code-block:: xml - + - - - - - + .. code-block:: php - // app/config/services.php + // config/services.php use Symfony\Component\DependencyInjection\Definition; // To use as default template @@ -915,16 +869,7 @@ key. For example, the default Symfony configuration contains this: ->setPublic(false) ; - $this->registerClasses($definition, 'AppBundle\\', '../../src/AppBundle/*', '../../src/AppBundle/{Entity,Repository}'); - - // Changes default config - $definition - ->setPublic(true) - ->addTag('controller.service_arguments') - ; - - // $this is a reference to the current loader - $this->registerClasses($definition, 'AppBundle\\Controller\\', '../../src/AppBundle/Controller/*'); + $this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Migrations,Tests}'); .. tip:: @@ -944,21 +889,18 @@ them will not cause the container to be rebuilt. .. note:: - Wait, does this mean that *every* class in ``src/AppBundle`` is registered as - a service? Even model or entity classes? Actually, no. As long as you have + Wait, does this mean that *every* class in ``src/`` is registered as + a service? Even model classes? Actually, no. As long as you have ``public: false`` under your ``_defaults`` key (or you can add it under the specific import), all the imported services are *private*. Thanks to this, all - classes in ``src/AppBundle`` that are *not* explicitly used as services are - automatically removed from the final container. In reality, the import simply + classes in ``src/`` that are *not* explicitly used as services are + automatically removed from the final container. In reality, the import means that all classes are "available to be *used* as services" without needing to be manually configured. Multiple Service Definitions Using the Same Namespace ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.4 - The ``namespace`` option in the YAML configuration was introduced in Symfony 3.4. - If you define services using the YAML config format, the PHP namespace is used as the key of each configuration, so you can't define different service configs for classes under the same namespace: @@ -1006,36 +948,36 @@ admin email. In this case, each needs to have a unique service id: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... # this is the service's id site_update_manager.superadmin: - class: AppBundle\Updates\SiteUpdateManager + class: App\Updates\SiteUpdateManager # you CAN still use autowiring: we just want to show what it looks like without autowire: false # manually wire all arguments arguments: - - '@AppBundle\Service\MessageGenerator' + - '@App\Service\MessageGenerator' - '@mailer' - 'superadmin@example.com' site_update_manager.normal_users: - class: AppBundle\Updates\SiteUpdateManager + class: App\Updates\SiteUpdateManager autowire: false arguments: - - '@AppBundle\Service\MessageGenerator' + - '@App\Service\MessageGenerator' - '@mailer' - 'contact@example.com' # Create an alias, so that - by default - if you type-hint SiteUpdateManager, # the site_update_manager.superadmin will be used - AppBundle\Updates\SiteUpdateManager: '@site_update_manager.superadmin' + App\Updates\SiteUpdateManager: '@site_update_manager.superadmin' .. code-block:: xml - + - - + + superadmin@example.com - - + + contact@example.com - + .. code-block:: php - // app/config/services.php - use AppBundle\Updates\SiteUpdateManager; - use AppBundle\Service\MessageGenerator; + // config/services.php + use App\Updates\SiteUpdateManager; + use App\Service\MessageGenerator; use Symfony\Component\DependencyInjection\Reference; $container->register('site_update_manager.superadmin', SiteUpdateManager::class) @@ -1093,7 +1035,7 @@ If you want to pass the second, you'll need to :ref:`manually wire the service < .. caution:: - If you do *not* create the alias and are :ref:`loading all services from src/AppBundle `, + If you do *not* create the alias and are :ref:`loading all services from src/ `, then *three* services have been created (the automatic service + your two services) and the automatically loaded service will be passed - by default - when you type-hint ``SiteUpdateManager``. That's why creating the alias is a good idea. @@ -1108,5 +1050,5 @@ Learn more /service_container/* .. _`service-oriented architecture`: https://en.wikipedia.org/wiki/Service-oriented_architecture -.. _`Symfony Standard Edition (version 3.3) services.yml`: https://github.com/symfony/symfony-standard/blob/3.3/app/config/services.yml +.. _`Symfony Standard Edition (version 3.3) services.yaml`: https://github.com/symfony/symfony-standard/blob/3.3/app/config/services.yml .. _`glob pattern`: https://en.wikipedia.org/wiki/Glob_(programming) diff --git a/service_container/3.3-di-changes.rst b/service_container/3.3-di-changes.rst index bad7ffe71f4..10529d5fa0f 100644 --- a/service_container/3.3-di-changes.rst +++ b/service_container/3.3-di-changes.rst @@ -1,7 +1,7 @@ The Symfony 3.3 DI Container Changes Explained (autowiring, _defaults, etc) =========================================================================== -If you look at the ``services.yml`` file in a new Symfony 3.3 project, you'll +If you look at the ``services.yaml`` file in a new Symfony 3.3 project, you'll notice some big changes: ``_defaults``, ``autowiring``, ``autoconfigure`` and more. These features are designed to *automate* configuration and make development faster, without sacrificing predictability, which is very important! Another goal is to make @@ -24,50 +24,44 @@ need to actually change your configuration files to use them. .. _`service-33-default_definition`: -The new Default services.yml File ---------------------------------- +The new Default services.yaml File +---------------------------------- -To understand the changes, look at the new default ``services.yml`` file in the -Symfony Standard Edition: +To understand the changes, look at the new default ``services.yaml`` file (this is +what the file looks like in Symfony 4): .. configuration-block:: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # default configuration for services in *this* file _defaults: - # automatically injects dependencies in your services - autowire: true - # automatically registers your services as commands, event subscribers, etc. - autoconfigure: true - # this means you cannot fetch services directly from the container via $container->get() - # if you need to do this, you can override this setting on individual services - public: false - - # makes classes in src/AppBundle available to be used as services - # this creates a service per class whose id is the fully-qualified class name - AppBundle\: - resource: '../../src/AppBundle/*' - # you can exclude directories or files - # but if a service is unused, it's removed anyway - exclude: '../../src/AppBundle/{Entity,Repository}' + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + public: false # Allows optimizing the container by removing unused services; this also means + # fetching services directly from the container via $container->get() won't work. + # The best practice is to be explicit about your dependencies anyway. - # controllers are imported separately to make sure they're public - # and have a tag that allows actions to type-hint services - AppBundle\Controller\: - resource: '../../src/AppBundle/Controller' + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\: + resource: '../src/*' + exclude: '../src/{Entity,Migrations,Tests}' + + # controllers are imported separately to make sure services can be injected + # as action arguments even if you don't extend any base controller class + App\Controller\: + resource: '../src/Controller' tags: ['controller.service_arguments'] - # add more services, or override services that need manual wiring - # AppBundle\Service\ExampleService: - # arguments: - # $someArgument: 'some_value' + # add more service definitions when explicit configuration is needed + # please note that last definitions always *replace* previous ones .. code-block:: xml - + - + - + @@ -106,21 +100,19 @@ thanks to the following config: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - # makes classes in src/AppBundle available to be used as services + # makes classes in src/ available to be used as services # this creates a service per class whose id is the fully-qualified class name - AppBundle\: - resource: '../../src/AppBundle/*' - # you can exclude directories or files - # but if a service is unused, it's removed anyway - exclude: '../../src/AppBundle/{Entity,Repository}' + App\: + resource: '../src/*' + exclude: '../src/{Entity,Migrations,Tests}' .. code-block:: xml - + - + -This means that every class in ``src/AppBundle/`` is *available* to be used as a +This means that every class in ``src/`` is *available* to be used as a service. And thanks to the ``_defaults`` section at the top of the file, all of these services are **autowired** and **private** (i.e. ``public: false``). -The service ids are equal to the class name (e.g. ``AppBundle\Service\InvoiceGenerator``). +The service ids are equal to the class name (e.g. ``App\Service\InvoiceGenerator``). And that's another change you'll notice in Symfony 3.3: we recommend that you use the class name as your service id, unless you have :ref:`multiple services for the same class `. @@ -149,7 +141,7 @@ to determine most arguments automatically. But, you can always override the serv and :ref:`manually configure arguments ` or anything else special about your service. - But wait, if I have some model (non-service) classes in my ``src/AppBundle/`` + But wait, if I have some model (non-service) classes in my ``src/`` directory, doesn't this mean that *they* will also be registered as services? Isn't that a problem? @@ -181,19 +173,19 @@ service as an argument to another with the following config: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: app.invoice_generator: - class: AppBundle\Service\InvoiceGenerator + class: App\Service\InvoiceGenerator app.invoice_mailer: - class: AppBundle\Service\InvoiceMailer + class: App\Service\InvoiceMailer arguments: - '@app.invoice_generator' .. code-block:: xml - + + class="App\Service\InvoiceGenerator" /> + class="App\Service\InvoiceMailer"> @@ -214,9 +206,9 @@ service as an argument to another with the following config: .. code-block:: php - // app/config/services.php - use AppBundle\Service\InvoiceGenerator; - use AppBundle\Service\InvoiceMailer; + // config/services.php + use App\Service\InvoiceGenerator; + use App\Service\InvoiceMailer; use Symfony\Component\DependencyInjection\Reference; $container->register('app.invoice_generator', InvoiceGenerator::class); @@ -230,7 +222,7 @@ id's were the main way that you configured things. But in Symfony 3.3, thanks to autowiring, all you need to do is type-hint the argument with ``InvoiceGenerator``:: - // src/AppBundle/Service/InvoiceMailer.php + // src/Service/InvoiceMailer.php // ... class InvoiceMailer @@ -247,8 +239,8 @@ argument with ``InvoiceGenerator``:: That's it! Both services are :ref:`automatically registered ` and set to autowire. Without *any* configuration, the container knows to pass the -auto-registered ``AppBundle\Service\InvoiceGenerator`` as the first argument. As -you can see, the *type* of the class - ``AppBundle\Service\InvoiceGenerator`` - is +auto-registered ``App\Service\InvoiceGenerator`` as the first argument. As +you can see, the *type* of the class - ``App\Service\InvoiceGenerator`` - is what's most important, not the id. You request an *instance* of a specific type and the container automatically passes you the correct service. @@ -283,9 +275,7 @@ was designed with that in mind. Specifically: * The container determines *which* service to pass in an explicit way: it looks for a service whose id matches the type-hint exactly. It does *not* scan all services - looking for objects that have that class/interface (actually, it *does* do this - in Symfony 3.3, but has been deprecated. If you rely on this, you will see a clear - deprecation warning). + looking for objects that have that class/interface. Autowiring aims to *automate* configuration without magic. @@ -298,19 +288,19 @@ The third big change is that, in a new Symfony 3.3 project, your controllers are .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... # controllers are imported separately to make sure they're public # and have a tag that allows actions to type-hint services - AppBundle\Controller\: - resource: '../../src/AppBundle/Controller' + App\Controller\: + resource: '../src/Controller' tags: ['controller.service_arguments'] .. code-block:: xml - + - + @@ -328,17 +318,17 @@ The third big change is that, in a new Symfony 3.3 project, your controllers are .. code-block:: php - // app/config/services.php + // config/services.php // ... $definition->addTag('controller.service_arguments'); - $this->registerClasses($definition, 'AppBundle\\Controller\\', '../../src/AppBundle/Controller/*'); + $this->registerClasses($definition, 'App\\Controller\\', '../src/Controller/*'); But, you might not even notice this. First, your controllers *can* still extend the same base ``Controller`` class or a new :ref:`AbstractController `. This means you have access to all of the same shortcuts as before. Additionally, -the ``@Route`` annotation and ``_controller`` syntax (e.g. ``AppBundle:Default:homepage``) +the ``@Route`` annotation and ``_controller`` syntax (e.g. ``App:Default:homepage``) used in routing will automatically use your controller as a service (as long as its service id matches its class name, which it *does* in this case). See :doc:`/controller/service` for more details. You can even create :ref:`invokable controllers ` @@ -369,6 +359,8 @@ In general, the new best practice is to use normal constructor dependency inject (or "action" injection in controllers) instead of fetching public services via ``$this->get()`` (though that does still work). +.. _service_autoconfigure: + 4) Auto-tagging with autoconfigure ---------------------------------- @@ -377,7 +369,7 @@ The fourth big change is the ``autoconfigure`` key, which is set to ``true`` und this file. For example, suppose you want to create an event subscriber. First, you create the class:: - // src/AppBundle/EventSubscriber/SetHeaderSusbcriber.php + // src/EventSubscriber/SetHeaderSusbcriber.php // ... use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -400,7 +392,7 @@ create the class:: } Great! In Symfony 3.2 or lower, you would now need to register this as a service -in ``services.yml`` and tag it with ``kernel.event_subscriber``. In Symfony 3.3, +in ``services.yaml`` and tag it with ``kernel.event_subscriber``. In Symfony 3.3, you're already done! The service is :ref:`automatically registered `. And thanks to ``autoconfigure``, Symfony automatically tags the service because it implements ``EventSubscriberInterface``. @@ -411,7 +403,7 @@ In this case, you've created a class that implements ``EventSubscriberInterface` and registered it as a service. This is more than enough for the container to know that you want this to be used as an event subscriber: more configuration is not needed. And the tags system is its own, Symfony-specific mechanism. And of course, you can -always set ``autoconfigure`` to ``false`` in ``services.yml``, or disable it for a specific +always set ``autoconfigure`` to ``false`` in ``services.yaml``, or disable it for a specific service. Does this mean tags are dead? Does this work for all tags? @@ -443,18 +435,18 @@ inherited from an abstract definition: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... _instanceof: - AppBundle\Domain\LoaderInterface: + App\Domain\LoaderInterface: public: true tags: ['app.domain_loader'] .. code-block:: xml - + - + @@ -493,23 +485,23 @@ Ready to upgrade your existing project? Great! Suppose you have the following co .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: app.github_notifier: - class: AppBundle\Service\GitHubNotifier + class: App\Service\GitHubNotifier arguments: - '@app.api_client_github' markdown_transformer: - class: AppBundle\Service\MarkdownTransformer + class: App\Service\MarkdownTransformer app.api_client_github: - class: AppBundle\Service\ApiClient + class: App\Service\ApiClient arguments: - 'https://api.github.com' app.api_client_sl_connect: - class: AppBundle\Service\ApiClient + class: App\Service\ApiClient arguments: - 'https://connect.sensiolabs.com/api' @@ -523,7 +515,7 @@ Start by adding a ``_defaults`` section with ``autowire`` and ``autoconfigure``. .. code-block:: diff - # app/config/services.yml + # config/services.yaml services: + _defaults: + autowire: true @@ -548,19 +540,19 @@ Start by updating the service ids to class names: .. code-block:: diff - # app/config/services.yml + # config/services.yaml services: # ... - app.github_notifier: - - class: AppBundle\Service\GitHubNotifier - + AppBundle\Service\GitHubNotifier: + - class: App\Service\GitHubNotifier + + App\Service\GitHubNotifier: arguments: - '@app.api_client_github' - markdown_transformer: - - class: AppBundle\Service\MarkdownTransformer - + AppBundle\Service\MarkdownTransformer: ~ + - class: App\Service\MarkdownTransformer + + App\Service\MarkdownTransformer: ~ # keep these ids because there are multiple instances per class app.api_client_github: @@ -578,7 +570,7 @@ Start by updating the service ids to class names: But, this change will break our app! The old service ids (e.g. ``app.github_notifier``) no longer exist. The simplest way to fix this is to find all your old service ids -and update them to the new class id: ``app.github_notifier`` to ``AppBundle\Service\GitHubNotifier``. +and update them to the new class id: ``app.github_notifier`` to ``App\Service\GitHubNotifier``. In large projects, there's a better way: create legacy aliases that map the old id to the new id. Create a new ``legacy_aliases.yml`` file: @@ -590,14 +582,14 @@ to the new id. Create a new ``legacy_aliases.yml`` file: # aliases so that the old service ids can still be accessed # remove these if/when you are not fetching these directly # from the container via $container->get() - app.github_notifier: '@AppBundle\Service\GitHubNotifier' - markdown_transformer: '@AppBundle\Service\MarkdownTransformer' + app.github_notifier: '@App\Service\GitHubNotifier' + markdown_transformer: '@App\Service\MarkdownTransformer' -Then import this at the top of ``services.yml``: +Then import this at the top of ``services.yaml``: .. code-block:: diff - # app/config/services.yml + # config/services.yaml + imports: + - { resource: legacy_aliases.yml } @@ -613,7 +605,7 @@ Now you're ready to default all services to be private: .. code-block:: diff - # app/config/services.yml + # config/services.yaml # ... services: @@ -631,7 +623,7 @@ instances of the same class), you may need to make those public: .. code-block:: diff - # app/config/services.yml + # config/services.yaml # ... services: @@ -655,29 +647,29 @@ clean that up. Step 4) Auto-registering Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You're now ready to automatically register all services in ``src/AppBundle/`` +You're now ready to automatically register all services in ``src/`` (and/or any other directory/bundle you have): .. code-block:: diff - # app/config/services.yml + # config/services.yaml services: _defaults: # ... - + AppBundle\: - + resource: '../../src/AppBundle/*' - + exclude: '../../src/AppBundle/{Entity,Repository}' + + App\: + + resource: '../src/*' + + exclude: '../src/{Entity,Migrations,Tests}' + - + AppBundle\Controller\: - + resource: '../../src/AppBundle/Controller' + + App\Controller\: + + resource: '../src/Controller' + tags: ['controller.service_arguments'] # ... That's it! Actually, you're already overriding and reconfiguring all the services -you're using (``AppBundle\Service\GitHubNotifier`` and ``AppBundle\Service\MarkdownTransformer``). +you're using (``App\Service\GitHubNotifier`` and ``App\Service\MarkdownTransformer``). But now, you won't need to manually register future services. Once again, there is one extra complication if you have multiple services of the @@ -685,14 +677,14 @@ same class: .. code-block:: diff - # app/config/services.yml + # config/services.yaml services: # ... + # alias ApiClient to one of our services below + # app.api_client_github will be used to autowire ApiClient type-hints - + AppBundle\Service\ApiClient: '@app.api_client_github' + + App\Service\ApiClient: '@app.api_client_github' app.api_client_github: # ... @@ -710,7 +702,7 @@ Step 5) Cleanup! To make sure your application didn't break, you did some extra work. Now it's time to clean things up! First, update your application to *not* use the old service id's (the ones in ``legacy_aliases.yml``). This means updating any service arguments (e.g. -``@app.github_notifier`` to ``@AppBundle\Service\GitHubNotifier``) and updating your +``@app.github_notifier`` to ``@App\Service\GitHubNotifier``) and updating your code to not fetch this service directly from the container. For example: .. code-block:: diff @@ -732,7 +724,7 @@ these directly from the container, you can remove the ``public: true`` flag: .. code-block:: diff - # app/config/services.yml + # config/services.yaml services: # ... @@ -744,7 +736,7 @@ these directly from the container, you can remove the ``public: true`` flag: # ... - public: true -Finally, you can optionally remove any services from ``services.yml`` whose arguments +Finally, you can optionally remove any services from ``services.yaml`` whose arguments can be autowired. The final configuration looks like this: .. code-block:: yaml @@ -755,31 +747,31 @@ can be autowired. The final configuration looks like this: autoconfigure: true public: false - AppBundle\: - resource: '../../src/AppBundle/*' - exclude: '../../src/AppBundle/{Entity,Repository}' + App\: + resource: '../src/*' + exclude: '../src/{Entity,Migrations,Tests}' - AppBundle\Controller\: - resource: '../../src/AppBundle/Controller' + App\Controller\: + resource: '../src/Controller' tags: ['controller.service_arguments'] - AppBundle\Service\GitHubNotifier: + App\Service\GitHubNotifier: # this could be deleted, or I can keep being explicit arguments: - '@app.api_client_github' # alias ApiClient to one of our services below # app.api_client_github will be used to autowire ApiClient type-hints - AppBundle\Service\ApiClient: '@app.api_client_github' + App\Service\ApiClient: '@app.api_client_github' # keep these ids because there are multiple instances per class app.api_client_github: - class: AppBundle\Service\ApiClient + class: App\Service\ApiClient arguments: - 'https://api.github.com' app.api_client_sl_connect: - class: AppBundle\Service\ApiClient + class: App\Service\ApiClient arguments: - 'https://connect.sensiolabs.com/api' diff --git a/service_container/alias_private.rst b/service_container/alias_private.rst index fd62298237a..8c87b31f793 100644 --- a/service_container/alias_private.rst +++ b/service_container/alias_private.rst @@ -23,7 +23,7 @@ And in this case, those services do *not* need to be public. So unless you *specifically* need to access a service directly from the container via ``$container->get()``, the best-practice is to make your services *private*. -In fact, the :ref:`default services.yml configuration ` configures +In fact, the :ref:`default services.yaml configuration ` configures all services to be private by default. You can also control the ``public`` option on a service-by-service basis: @@ -32,27 +32,30 @@ You can also control the ``public`` option on a service-by-service basis: .. code-block:: yaml + # config/services.yaml services: # ... - AppBundle\Service\Foo: + App\Service\Foo: public: false .. code-block:: xml + - + .. code-block:: php - use AppBundle\Service\Foo; + // config/services.php + use App\Service\Foo; $container->register(Foo::class) ->setPublic(false); @@ -68,7 +71,7 @@ not have run on that page. Now that the service is private, you *must not* fetch the service directly from the container:: - use AppBundle\Service\Foo; + use App\Service\Foo; $container->get(Foo::class); @@ -78,11 +81,6 @@ it directly from your code. However, if a service has been marked as private, you can still alias it (see below) to access this service (via the alias). -.. note:: - - Services are by default public, but it's considered a good practice to mark - as many services private as possible. - .. _services-alias: Aliasing @@ -96,17 +94,19 @@ services. .. code-block:: yaml + # config/services.yaml services: # ... - AppBundle\Mail\PhpMailer: + App\Mail\PhpMailer: public: false app.mailer: - alias: AppBundle\Mail\PhpMailer + alias: App\Mail\PhpMailer public: true .. code-block:: xml + - + - + .. code-block:: php - use AppBundle\Mail\PhpMailer; + // config/services.php + use App\Mail\PhpMailer; $container->register(PhpMailer::class) ->setPublic(false); @@ -140,9 +141,10 @@ This means that when using the container directly, you can access the .. code-block:: yaml + # config/services.yaml services: # ... - app.mailer: '@AppBundle\Mail\PhpMailer' + app.mailer: '@App\Mail\PhpMailer' Deprecating Services -------------------- @@ -154,18 +156,20 @@ or you decided not to maintain it anymore), you can deprecate its definition: .. code-block:: yaml - AppBundle\Service\OldService: - deprecated: The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0. + # config/services.yaml + App\Service\OldService: + deprecated: The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0. .. code-block:: xml + - + The "%service_id%" service is deprecated since 2.8 and will be removed in 3.0. @@ -173,7 +177,8 @@ or you decided not to maintain it anymore), you can deprecate its definition: .. code-block:: php - use AppBundle\Service\OldService; + // config/services.php + use App\Service\OldService; $container ->register(OldService::class) diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index d91ef3f52e2..91a434dee5c 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -24,7 +24,7 @@ the alphabet. Start by creating a ROT13 transformer class:: - namespace AppBundle\Util; + namespace App\Util; class Rot13Transformer { @@ -36,9 +36,9 @@ Start by creating a ROT13 transformer class:: And now a Twitter client using this transformer:: - namespace AppBundle\Service; + namespace App\Service; - use AppBundle\Util\Rot13Transformer; + use App\Util\Rot13Transformer; class TwitterClient { @@ -57,7 +57,7 @@ And now a Twitter client using this transformer:: } } -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, **both classes are automatically registered as services and configured to be autowired**. This means you can use them immediately without *any* configuration. @@ -69,6 +69,7 @@ both services. Also, to keep things simple, configure ``TwitterClient`` to be a .. code-block:: yaml + # config/services.yaml services: _defaults: autowire: true @@ -76,17 +77,18 @@ both services. Also, to keep things simple, configure ``TwitterClient`` to be a public: false # ... - AppBundle\Service\TwitterClient: + App\Service\TwitterClient: # redundant thanks to _defaults, but value is overridable on each service autowire: true # not required, will help in our example public: true - AppBundle\Util\Rot13Transformer: + App\Util\Rot13Transformer: autowire: true .. code-block:: xml + - + - + .. code-block:: php - use AppBundle\Service\TwitterClient; - use AppBundle\Util\Rot13Transformer; + // config/services.php + use App\Service\TwitterClient; + use App\Util\Rot13Transformer; // ... @@ -119,9 +122,9 @@ both services. Also, to keep things simple, configure ``TwitterClient`` to be a Now, you can use the ``TwitterClient`` service immediately in a controller:: - namespace AppBundle\Controller; + namespace App\Controller; - use AppBundle\Service\TwitterClient; + use App\Service\TwitterClient; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\Routing\Annotation\Route; @@ -130,7 +133,7 @@ Now, you can use the ``TwitterClient`` service immediately in a controller:: /** * @Route("/tweet") */ - public function tweetAction() + public function tweet() { // fetch $user, $key, $status from the POST'ed data @@ -152,7 +155,7 @@ Autowiring Logic Explained Autowiring works by reading the ``Rot13Transformer`` *type-hint* in ``TwitterClient``:: // ... - use AppBundle\Util\Rot13Transformer; + use App\Util\Rot13Transformer; class TwitterClient { @@ -165,29 +168,14 @@ Autowiring works by reading the ``Rot13Transformer`` *type-hint* in ``TwitterCli } The autowiring system **looks for a service whose id exactly matches the type-hint**: -so ``AppBundle\Util\Rot13Transformer``. In this case, that exists! When you configured +so ``App\Util\Rot13Transformer``. In this case, that exists! When you configured the ``Rot13Transformer`` service, you used its fully-qualified class name as its id. Autowiring isn't magic: it simply looks for a service whose id matches the type-hint. If you :ref:`load services automatically `, -each service's id is its class name. This is the main way to control autowiring. +each service's id is its class name. -If there is *not* a service whose id exactly matches the type, then: - -If there are **0** services in the container that have the type, then: - If the type is a concrete class, then a new, private, autowired service is - auto-registered in the container and used for the argument. - -.. _autowiring-single-matching-service: - -If there is exactly **1** service in the container that has the type, then: - (deprecated) This service is used for the argument. In Symfony 4.0, this - will be removed. The proper solution is to create an :ref:`alias ` - from the type to the service id so that normal autowiring works. - -If there are **2 or more** services in the container that have the type, then: - A clear exception is thrown. You need to *choose* which service should - be used by creating an :ref:`alias ` or - :ref:`configuring the argument explicitly `. +If there is *not* a service whose id exactly matches the type, a clear exception +will be thrown. Autowiring is a great way to automate configuration, and Symfony tries to be as *predictable* and clear as possible. @@ -198,13 +186,13 @@ Using Aliases to Enable Autowiring ---------------------------------- The main way to configure autowiring is to create a service whose id exactly matches -its class. In the previous example, the service's id is ``AppBundle\Util\Rot13Transformer``, +its class. In the previous example, the service's id is ``App\Util\Rot13Transformer``, which allows us to autowire this type automatically. This can also be accomplished using an :ref:`alias `. Suppose that for some reason, the id of the service was instead ``app.rot13.transformer``. In -this case, any arguments type-hinted with the class name (``AppBundle\Util\Rot13Transformer``) -can no longer be autowired (actually, it :ref:`will work now, but not in Symfony 4.0 `). +this case, any arguments type-hinted with the class name (``App\Util\Rot13Transformer``) +can no longer be autowired. No problem! To fix this, you can *create* a service whose id matches the class by adding a service alias: @@ -213,21 +201,23 @@ adding a service alias: .. code-block:: yaml + # config/services.yaml services: # ... # the id is not a class, so it won't be used for autowiring app.rot13.transformer: - class: AppBundle\Util\Rot13Transformer + class: App\Util\Rot13Transformer # ... # but this fixes it! # the ``app.rot13.transformer`` service will be injected when - # an ``AppBundle\Util\Rot13Transformer`` type-hint is detected - AppBundle\Util\Rot13Transformer: '@app.rot13.transformer' + # an ``App\Util\Rot13Transformer`` type-hint is detected + App\Util\Rot13Transformer: '@app.rot13.transformer' .. code-block:: xml + - - + + .. code-block:: php - use AppBundle\Util\Rot13Transformer; + // config/services.php + use App\Util\Rot13Transformer; // ... @@ -251,7 +242,7 @@ adding a service alias: ->setPublic(false); $container->setAlias(Rot13Transformer::class, 'app.rot13.transformer'); -This creates a service "alias", whose id is ``AppBundle\Util\Rot13Transformer``. +This creates a service "alias", whose id is ``App\Util\Rot13Transformer``. Thanks to this, autowiring sees this and uses it whenever the ``Rot13Transformer`` class is type-hinted. @@ -273,7 +264,7 @@ objects. To follow this best practice, suppose you decide to create a ``TransformerInterface``:: - namespace AppBundle\Util; + namespace App\Util; interface TransformerInterface { @@ -300,10 +291,9 @@ Now that you have an interface, you should use this as your type-hint:: // ... } -But now, the type-hint (``AppBundle\Util\TransformerInterface``) no longer matches -the id of the service (``AppBundle\Util\Rot13Transformer``). This means that the -argument can no longer be autowired (actually, it -:ref:`will work now, but not in Symfony 4.0 `). +But now, the type-hint (``App\Util\TransformerInterface``) no longer matches +the id of the service (``App\Util\Rot13Transformer``). This means that the +argument can no longer be autowired. To fix that, add an :ref:`alias `: @@ -311,17 +301,19 @@ To fix that, add an :ref:`alias `: .. code-block:: yaml + # config/services.yaml services: # ... - AppBundle\Util\Rot13Transformer: ~ + App\Util\Rot13Transformer: ~ - # the ``AppBundle\Util\Rot13Transformer`` service will be injected when - # an ``AppBundle\Util\TransformerInterface`` type-hint is detected - AppBundle\Util\TransformerInterface: '@AppBundle\Util\Rot13Transformer' + # the ``App\Util\Rot13Transformer`` service will be injected when + # an ``App\Util\TransformerInterface`` type-hint is detected + App\Util\TransformerInterface: '@App\Util\Rot13Transformer' .. code-block:: xml + `: - + - + .. code-block:: php - use AppBundle\Util\Rot13Transformer; - use AppBundle\Util\TransformerInterface; + // config/services.php + use App\Util\Rot13Transformer; + use App\Util\TransformerInterface; // ... $container->autowire(Rot13Transformer::class); $container->setAlias(TransformerInterface::class, Rot13Transformer::class); -Thanks to the ``AppBundle\Util\TransformerInterface`` alias, the autowiring subsystem -knows that the ``AppBundle\Util\Rot13Transformer`` service should be injected when +Thanks to the ``App\Util\TransformerInterface`` alias, the autowiring subsystem +knows that the ``App\Util\Rot13Transformer`` service should be injected when dealing with the ``TransformerInterface``. Dealing with Multiple Implementations of the Same Type @@ -354,7 +347,7 @@ Dealing with Multiple Implementations of the Same Type Suppose you create a second class - ``UppercaseTransformer`` that implements ``TransformerInterface``:: - namespace AppBundle\Util; + namespace App\Util; class UppercaseTransformer implements TransformerInterface { @@ -365,7 +358,7 @@ Suppose you create a second class - ``UppercaseTransformer`` that implements } If you register this as a service, you now have *two* services that implement the -``AppBundle\Util\TransformerInterface`` type. Symfony doesn't know which one should +``App\Util\TransformerInterface`` type. Symfony doesn't know which one should be used for autowiring, so you need to choose one by creating an alias from the type to the correct service id (see :ref:`autowiring-interface-alias`). @@ -376,27 +369,29 @@ that alias: .. code-block:: yaml + # config/services.yaml services: # ... - AppBundle\Util\Rot13Transformer: ~ - AppBundle\Util\UppercaseTransformer: ~ + App\Util\Rot13Transformer: ~ + App\Util\UppercaseTransformer: ~ - # the ``AppBundle\Util\Rot13Transformer`` service will be injected when - # a ``AppBundle\Util\TransformerInterface`` type-hint is detected - AppBundle\Util\TransformerInterface: '@AppBundle\Util\Rot13Transformer' + # the ``App\Util\Rot13Transformer`` service will be injected when + # a ``App\Util\TransformerInterface`` type-hint is detected + App\Util\TransformerInterface: '@App\Util\Rot13Transformer' - AppBundle\Service\TwitterClient: + App\Service\TwitterClient: # the Rot13Transformer will be passed as the $transformer argument autowire: true # If you wanted to choose the non-default service, wire it manually # arguments: - # $transformer: '@AppBundle\Util\UppercaseTransformer' + # $transformer: '@App\Util\UppercaseTransformer' # ... .. code-block:: xml + - - + + - + - - + + .. code-block:: php - use AppBundle\Util\Rot13Transformer; - use AppBundle\Util\UppercaseTransformer; - use AppBundle\Util\TransformerInterface; - use AppBundle\Service\TwitterClient; + // config/services.php + use App\Util\Rot13Transformer; + use App\Util\UppercaseTransformer; + use App\Util\TransformerInterface; + use App\Service\TwitterClient; // ... $container->autowire(Rot13Transformer::class); @@ -430,15 +426,11 @@ that alias: //->setArgument('$transformer', new Reference(UppercaseTransformer::class)) ; -Thanks to the ``AppBundle\Util\TransformerInterface`` alias, any argument type-hinted -with this interface will be passed the ``AppBundle\Util\Rot13Transformer`` service. +Thanks to the ``App\Util\TransformerInterface`` alias, any argument type-hinted +with this interface will be passed the ``App\Util\Rot13Transformer`` service. But, you can also manually wire the *other* service by specifying the argument under the arguments key. -.. versionadded:: 3.3 - Using FQCN aliases to fix autowiring ambiguities was introduced in Symfony - 3.3. Prior to version 3.3, you needed to use the ``autowiring_types`` key. - Fixing Non-Autowireable Arguments --------------------------------- @@ -458,7 +450,7 @@ When autowiring is enabled for a service, you can *also* configure the container to call methods on your class when it's instantiated. For example, suppose you want to inject the ``logger`` service, and decide to use setter-injection:: - namespace AppBundle\Util; + namespace App\Util; class Rot13Transformer { diff --git a/service_container/calls.rst b/service_container/calls.rst index 85fc04571d3..6807f30f9fe 100644 --- a/service_container/calls.rst +++ b/service_container/calls.rst @@ -13,7 +13,7 @@ Usually, you'll want to inject your dependencies via the constructor. But someti especially if a dependency is optional, you may want to use "setter injection". For example:: - namespace AppBundle\Service; + namespace App\Service; use Psr\Log\LoggerInterface; @@ -35,9 +35,9 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\Service\MessageGenerator: + App\Service\MessageGenerator: # ... calls: - method: setLogger @@ -46,7 +46,7 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k .. code-block:: xml - + - + @@ -65,8 +65,8 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k .. code-block:: php - // app/config/services.php - use AppBundle\Service\MessageGenerator; + // config/services.php + use App\Service\MessageGenerator; use Symfony\Component\DependencyInjection\Reference; $container->register(MessageGenerator::class) diff --git a/service_container/compiler_passes.rst b/service_container/compiler_passes.rst index d93ddd3d01b..7b029ab8850 100644 --- a/service_container/compiler_passes.rst +++ b/service_container/compiler_passes.rst @@ -2,26 +2,84 @@ single: DependencyInjection; Compiler passes single: Service Container; Compiler passes -How to Work with Compiler Passes in Bundles -=========================================== +How to Work with Compiler Passes +================================ Compiler passes give you an opportunity to manipulate other service definitions that have been registered with the service container. You can read about how to create them in the components section ":ref:`components-di-separate-compiler-passes`". -When using :ref:`separate compiler passes `, -you need to register them in the ``build()`` method of the bundle class (this -is not needed when implementing the ``process()`` method in the extension):: +Compiler passes are registered in the ``build()`` method of the application kernel:: - // src/AppBundle/AppBundle.php - namespace AppBundle; + // src/Kernel.php + namespace App; + + use App\DependencyInjection\Compiler\CustomPass; + use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; + + class Kernel extends BaseKernel + { + use MicroKernelTrait; + + // ... + + protected function build(ContainerBuilder $container): void + { + $container->addCompilerPass(new CustomPass()); + } + } + +One of the most common use-cases of compiler passes is to work with :doc:`tagged +services `. In those cases, instead of creating a +compiler pass, you can make the kernel implement +:class:`Symfony\\Component\\DependencyInjection\\Compiler\\CompilerPassInterface` +and process the services inside the ``process()`` method:: + + // src/Kernel.php + namespace App; + + use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; + use Symfony\Component\DependencyInjection\ContainerBuilder; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; + + class Kernel extends BaseKernel implements CompilerPassInterface + { + use MicroKernelTrait; + + // ... + + public function process(ContainerBuilder $container) + { + // in this method you can manipulate the service container: + // for example, changing some container service: + $container->findDefinition('app.some_private_service')->setPublic(true); + + // or processing tagged services: + foreach ($container->findTaggedServiceIds('some_tag') as $id => $tags) { + // ... + } + } + } + +Working with Compiler Passes in Bundles +--------------------------------------- + +`Bundles `_ can define compiler passes in the ``build()`` method of +the main bundle class (this is not needed when implementing the ``process()`` +method in the extension):: + + // src/MyBundle/MyBundle.php + namespace App\MyBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; - use AppBundle\DependencyInjection\Compiler\CustomPass; + use App\DependencyInjection\Compiler\CustomPass; - class AppBundle extends Bundle + class MyBundle extends Bundle { public function build(ContainerBuilder $container) { @@ -31,10 +89,8 @@ is not needed when implementing the ``process()`` method in the extension):: } } -One of the most common use-cases of compiler passes is to work with tagged services -(read more about tags in ":doc:`/service_container/tags`"). If you are using -custom tags in a bundle then by convention, tag names consist of the name of -the bundle (lowercase, underscores as separators), followed by a dot, and -finally the "real" name. For example, if you want to introduce some sort of -"transport" tag in your AcmeMailerBundle, you should call it -``acme_mailer.transport``. +If you are using custom service tags in a bundle then by convention, tag names +consist of the name of the bundle (lowercase, underscores as separators), +followed by a dot, and finally the "real" name. For example, if you want to +introduce some sort of "transport" tag in your AcmeMailerBundle, you should call +it ``acme_mailer.transport``. diff --git a/service_container/configurators.rst b/service_container/configurators.rst index b4d7611ed0e..9892e7aa377 100644 --- a/service_container/configurators.rst +++ b/service_container/configurators.rst @@ -21,8 +21,8 @@ of emails to users. Emails are passed through different formatters that could be enabled or not depending on some dynamic application settings. You start defining a ``NewsletterManager`` class like this:: - // src/AppBundle/Mail/NewsletterManager.php - namespace AppBundle\Mail; + // src/Mail/NewsletterManager.php + namespace App\Mail; class NewsletterManager implements EmailFormatterAwareInterface { @@ -38,8 +38,8 @@ You start defining a ``NewsletterManager`` class like this:: and also a ``GreetingCardManager`` class:: - // src/AppBundle/Mail/GreetingCardManager.php - namespace AppBundle\Mail; + // src/Mail/GreetingCardManager.php + namespace App\Mail; class GreetingCardManager implements EmailFormatterAwareInterface { @@ -58,8 +58,8 @@ on application settings. To do this, you also have an ``EmailFormatterManager`` class which is responsible for loading and validating formatters enabled in the application:: - // src/AppBundle/Mail/EmailFormatterManager.php - namespace AppBundle\Mail; + // src/Mail/EmailFormatterManager.php + namespace App\Mail; class EmailFormatterManager { @@ -80,8 +80,8 @@ If your goal is to avoid having to couple ``NewsletterManager`` and ``GreetingCardManager`` with ``EmailFormatterManager``, then you might want to create a configurator class to configure these instances:: - // src/AppBundle/Mail/EmailConfigurator.php - namespace AppBundle\Mail; + // src/Mail/EmailConfigurator.php + namespace App\Mail; class EmailConfigurator { @@ -117,7 +117,7 @@ Using the Configurator ---------------------- You can configure the service configurator using the ``configurator`` option. If -you're using the :ref:`default services.yml configuration `, +you're using the :ref:`default services.yaml configuration `, all the classes are already loaded as services. All you need to do is specify the ``configurator``: @@ -125,25 +125,25 @@ all the classes are already loaded as services. All you need to do is specify th .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - # Registers all 4 classes as services, including AppBundle\Mail\EmailConfigurator - AppBundle\: - resource: '../../src/AppBundle/*' + # Registers all 4 classes as services, including App\Mail\EmailConfigurator + App\: + resource: '../src/*' # ... # override the services to set the configurator - AppBundle\Mail\NewsletterManager: - configurator: 'AppBundle\Mail\EmailConfigurator:configure' + App\Mail\NewsletterManager: + configurator: 'App\Mail\EmailConfigurator:configure' - AppBundle\Mail\GreetingCardManager: - configurator: 'AppBundle\Mail\EmailConfigurator:configure' + App\Mail\GreetingCardManager: + configurator: 'App\Mail\EmailConfigurator:configure' .. code-block:: xml - + - + - - + + - - + + .. code-block:: php - // app/config/services.php - use AppBundle\Mail\GreetingCardManager; - use AppBundle\Mail\NewsletterManager; + // config/services.php + use App\Mail\GreetingCardManager; + use App\Mail\NewsletterManager; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -176,7 +176,7 @@ all the classes are already loaded as services. All you need to do is specify th $definition->setAutowired(true); - $this->registerClasses($definition, 'AppBundle\\', '../../src/AppBundle/*'); + $this->registerClasses($definition, 'App\\', '../src/*'); $container->getDefinition(NewsletterManager::class) ->setConfigurator(array(new Reference(EmailConfigurator::class), 'configure')); @@ -184,21 +184,17 @@ all the classes are already loaded as services. All you need to do is specify th $container->getDefinition(GreetingCardManager::class) ->setConfigurator(array(new Reference(EmailConfigurator::class), 'configure')); -.. versionadded:: 3.2 - The ``service_id:method_name`` syntax for the YAML configuration format - was introduced in Symfony 3.2. +The traditional configurator syntax in YAML files used an array to define +the service id and the method name: - The traditional configurator syntax in YAML files used an array to define - the service id and the method name: +.. code-block:: yaml - .. code-block:: yaml - - app.newsletter_manager: - # new syntax - configurator: 'AppBundle\Mail\EmailConfigurator:configure' - # old syntax - configurator: ['@AppBundle\Mail\EmailConfigurator', configure] + app.newsletter_manager: + # new syntax + configurator: 'App\Mail\EmailConfigurator:configure' + # old syntax + configurator: ['@App\Mail\EmailConfigurator', configure] -That's it! When requesting the ``AppBundle\Mail\NewsletterManager`` or -``AppBundle\Mail\GreetingCardManager`` service, the created instance will first be +That's it! When requesting the ``App\Mail\NewsletterManager`` or +``App\Mail\GreetingCardManager`` service, the created instance will first be passed to the ``EmailConfigurator::configure()`` method. diff --git a/service_container/debug.rst b/service_container/debug.rst index f235b38392e..9e00b322981 100644 --- a/service_container/debug.rst +++ b/service_container/debug.rst @@ -24,9 +24,6 @@ To see a list of all of the available types that can be used for autowiring, run $ php bin/console debug:autowiring -.. versionadded:: 3.4 - The ``debug:autowiring`` command was introduced in Symfony 3.3. - Detailed Info about a Single Service ------------------------------------ @@ -35,10 +32,7 @@ its id: .. code-block:: terminal - $ php bin/console debug:container 'AppBundle\Service\Mailer' + $ php bin/console debug:container 'App\Service\Mailer' # to show the service arguments: - $ php bin/console debug:container 'AppBundle\Service\Mailer' --show-arguments - -.. versionadded:: 3.3 - The ``--show-arguments`` option was introduced in Symfony 3.3. + $ php bin/console debug:container 'App\Service\Mailer' --show-arguments diff --git a/service_container/definitions.rst b/service_container/definitions.rst index 29db23cd090..7462f6639f9 100644 --- a/service_container/definitions.rst +++ b/service_container/definitions.rst @@ -32,14 +32,14 @@ There are some helpful methods for working with the service definitions:: $definition = $container->findDefinition('app.user_config_manager'); // adds a new "app.number_generator" definition - $definition = new Definition(\AppBundle\NumberGenerator::class); + $definition = new Definition(\App\NumberGenerator::class); $container->setDefinition('app.number_generator', $definition); // shortcut for the previous method - $container->register('app.number_generator', \AppBundle\NumberGenerator::class); + $container->register('app.number_generator', \App\NumberGenerator::class); // or create a service whose id matches its class - $container->register(\AppBundle\NumberGenerator::class); + $container->register(\App\NumberGenerator::class); Working with a Definition ------------------------- @@ -57,8 +57,8 @@ Class The first optional argument of the ``Definition`` class is the fully qualified class name of the object returned when the service is fetched from the container:: - use AppBundle\Config\UserConfigManager; - use AppBundle\Config\CustomConfigManager; + use App\Config\UserConfigManager; + use App\Config\CustomConfigManager; use Symfony\Component\DependencyInjection\Definition; $definition = new Definition(UserConfigManager::class); @@ -76,7 +76,7 @@ The second optional argument of the ``Definition`` class is an array with the arguments passed to the constructor of the object returned when the service is fetched from the container:: - use AppBundle\Config\DoctrineConfigManager; + use App\Config\DoctrineConfigManager; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index 8f0214b29ec..4aa57599c34 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -10,29 +10,29 @@ How to Inject Values Based on Complex Expressions The service container also supports an "expression" that allows you to inject very specific values into a service. -For example, suppose you have a service (not shown here), called ``AppBundle\Mail\MailerConfiguration``, +For example, suppose you have a service (not shown here), called ``App\Mail\MailerConfiguration``, which has a ``getMailerMethod()`` method on it. This returns a string - like ``sendmail`` based on some configuration. Suppose that you want to pass the result of this method as a constructor argument -to another service: ``AppBundle\Mailer``. One way to do this is with an expression: +to another service: ``App\Mailer``. One way to do this is with an expression: .. configuration-block:: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - AppBundle\Mail\MailerConfiguration: ~ + App\Mail\MailerConfiguration: ~ - AppBundle\Mailer: - arguments: ["@=service('AppBundle\\\\Mail\\\\MailerConfiguration').getMailerMethod()"] + App\Mailer: + arguments: ["@=service('App\\\\Mail\\\\MailerConfiguration').getMailerMethod()"] .. code-block:: xml - + - + - + service('App\\Mail\\MailerConfiguration').getMailerMethod() @@ -52,15 +52,15 @@ to another service: ``AppBundle\Mailer``. One way to do this is with an expressi .. code-block:: php - // app/config/services.php - use AppBundle\Mail\MailerConfiguration; - use AppBundle\Mailer; + // config/services.php + use App\Mail\MailerConfiguration; + use App\Mailer; use Symfony\Component\ExpressionLanguage\Expression; $container->autowire(MailerConfiguration::class); $container->autowire(Mailer::class) - ->addArgument(new Expression('service("AppBundle\\\\Mail\\\\MailerConfiguration").getMailerMethod()')); + ->addArgument(new Expression('service("App\\\\Mail\\\\MailerConfiguration").getMailerMethod()')); To learn more about the expression language syntax, see :doc:`/components/expression_language/syntax`. @@ -78,12 +78,14 @@ via a ``container`` variable. Here's another example: .. code-block:: yaml + # config/services.yaml services: - AppBundle\Mailer: + App\Mailer: arguments: ["@=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'"] .. code-block:: xml + - + container.hasParameter('some_param') ? parameter('some_param') : 'default_value' @@ -99,7 +101,8 @@ via a ``container`` variable. Here's another example: .. code-block:: php - use AppBundle\Mailer; + // config/services.php + use App\Mailer; use Symfony\Component\ExpressionLanguage\Expression; $container->autowire(Mailer::class) diff --git a/service_container/factories.rst b/service_container/factories.rst index 6fd6df909ec..cfca74e26df 100644 --- a/service_container/factories.rst +++ b/service_container/factories.rst @@ -35,18 +35,17 @@ configure the service container to use the .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: # ... - AppBundle\Email\NewsletterManager: + App\Email\NewsletterManager: # call the static method - factory: ['AppBundle\Email\NewsletterManagerStaticFactory', createNewsletterManager] + factory: ['App\Email\NewsletterManagerStaticFactory', createNewsletterManager] .. code-block:: xml - - + - + - + - @@ -69,11 +68,10 @@ configure the service container to use the .. code-block:: php - // app/config/services.php - - use AppBundle\Email\NewsletterManager; - use AppBundle\NumberGenerator; - use AppBundle\Email\NewsletterManagerStaticFactory; + // config/services.php + use App\Email\NewsletterManager; + use App\NumberGenerator; + use App\Email\NewsletterManagerStaticFactory; // ... $container->register(NumberGenerator::class) @@ -99,21 +97,19 @@ Configuration of the service container then looks like this: .. code-block:: yaml - # app/config/services.yml - + # config/services.yaml services: # ... - AppBundle\Email\NewsletterManagerFactory: ~ + App\Email\NewsletterManagerFactory: ~ - AppBundle\Email\NewsletterManager: + App\Email\NewsletterManager: # call a method on the specified factory service - factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager' + factory: 'App\Email\NewsletterManagerFactory:createNewsletterManager' .. code-block:: xml - - + - + - + - @@ -134,10 +130,9 @@ Configuration of the service container then looks like this: .. code-block:: php - // app/config/services.php - - use AppBundle\Email\NewsletterManager; - use AppBundle\Email\NewsletterManagerFactory; + // config/services.php + use App\Email\NewsletterManager; + use App\Email\NewsletterManagerFactory; // ... $container->register(NewsletterManagerFactory::class); @@ -156,13 +151,12 @@ Configuration of the service container then looks like this: .. code-block:: yaml - # app/config/services.yml - - AppBundle\Email\NewsletterManager: + # config/services.yaml + App\Email\NewsletterManager: # new syntax - factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager' + factory: 'App\Email\NewsletterManagerFactory:createNewsletterManager' # old syntax - factory: ['@AppBundle\Email\NewsletterManagerFactory', createNewsletterManager] + factory: ['@App\Email\NewsletterManagerFactory', createNewsletterManager] .. _factories-passing-arguments-factory-method: @@ -182,19 +176,17 @@ example takes the ``templating`` service as an argument: .. code-block:: yaml - # app/config/services.yml - + # config/services.yaml services: # ... - AppBundle\Email\NewsletterManager: - factory: 'AppBundle\Email\NewsletterManagerFactory:createNewsletterManager' + App\Email\NewsletterManager: + factory: 'App\Email\NewsletterManagerFactory:createNewsletterManager' arguments: ['@templating'] .. code-block:: xml - - + - - + + @@ -213,10 +205,9 @@ example takes the ``templating`` service as an argument: .. code-block:: php - // app/config/services.php - - use AppBundle\Email\NewsletterManager; - use AppBundle\Email\NewsletterManagerFactory; + // config/services.php + use App\Email\NewsletterManager; + use App\Email\NewsletterManagerFactory; use Symfony\Component\DependencyInjection\Reference; // ... diff --git a/service_container/import.rst b/service_container/import.rst index 8c98b43aa84..cc622ca4f7b 100644 --- a/service_container/import.rst +++ b/service_container/import.rst @@ -13,10 +13,8 @@ How to Import Configuration Files/Resources web service). The service container is built using a single configuration resource -(``app/config/config.yml`` by default). All other service configuration -(including the core Symfony and third-party bundle configuration) must -be imported from inside this file in one way or another. This gives you absolute -flexibility over the services in your application. +(``config/services.yaml`` by default). This gives you absolute flexibility over +the services in your application. External service configuration can be imported in two different ways. The first method, commonly used to import other resources, is via the ``imports`` @@ -32,7 +30,7 @@ methods. Importing Configuration with ``imports`` ---------------------------------------- -By default, service configuration lives in ``app/config/services.yml``. But if that +By default, service configuration lives in ``config/services.yaml``. But if that file becomes large, you're free to organize into multiple files. For suppose you decided to move some configuration to a new file: @@ -40,7 +38,7 @@ decided to move some configuration to a new file: .. code-block:: yaml - # app/config/services/mailer.yml + # config/services/mailer.yaml parameters: # ... some parameters @@ -49,7 +47,7 @@ decided to move some configuration to a new file: .. code-block:: xml - + + import('services/mailer.php'); The ``resource`` location, for files, is either a relative path from the @@ -115,7 +113,7 @@ Importing Configuration via Container Extensions ------------------------------------------------ Third-party bundle container configuration, including Symfony core services, -are usually loaded using another method: a container extension. +are usually loaded using another method: a :doc:`container extension `. Internally, each bundle defines its services in files like you've seen so far. However, these files aren't imported using the ``import`` directive. Instead, bundles @@ -123,10 +121,6 @@ use a *dependency injection extension* to load the files automatically. As soon as you enable a bundle, its extension is called, which is able to load service configuration files. -In fact, each configuration block in ``config.yml`` - e.g. ``framework`` or ``twig``- -is passed to the extension for that bundle - e.g. ``FrameworkBundle`` or ``TwigBundle`` - +In fact, each configuration file in ``config/packages/`` is passed to the +extension of its related bundle - e.g. ``FrameworkBundle`` or ``TwigBundle`` - and used to configure those services further. - -If you want to use dependency injection extensions in your own shared -bundles and provide user friendly configuration, take a look at the -:doc:`/bundles/extension` article. diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst index d2afc93b044..60478f66a5f 100644 --- a/service_container/injection_types.rst +++ b/service_container/injection_types.rst @@ -19,7 +19,7 @@ The most common way to inject dependencies is via a class's constructor. To do this you need to add an argument to the constructor signature to accept the dependency:: - namespace AppBundle\Mail; + namespace App\Mail; // ... class NewsletterManager @@ -41,14 +41,16 @@ service container configuration: .. code-block:: yaml - services: + # config/services.yaml + services: # ... - AppBundle\Mail\NewsletterManager: + App\Mail\NewsletterManager: arguments: ['@mailer'] .. code-block:: xml + - + @@ -66,7 +68,8 @@ service container configuration: .. code-block:: php - use AppBundle\Mail\NewsletterManager; + // config/services.php + use App\Mail\NewsletterManager; use Symfony\Component\DependencyInjection\Reference; // ... @@ -120,16 +123,18 @@ that accepts the dependency:: .. code-block:: yaml + # config/services.yaml services: # ... app.newsletter_manager: - class: AppBundle\Mail\NewsletterManager + class: App\Mail\NewsletterManager calls: - [setMailer, ['@mailer']] .. code-block:: xml + - + @@ -149,7 +154,8 @@ that accepts the dependency:: .. code-block:: php - use AppBundle\Mail\NewsletterManager; + // config/services.php + use App\Mail\NewsletterManager; use Symfony\Component\DependencyInjection\Reference; // ... @@ -192,16 +198,18 @@ Another possibility is just setting public fields of the class directly:: .. code-block:: yaml + # config/services.yaml services: # ... app.newsletter_manager: - class: AppBundle\Mail\NewsletterManager + class: App\Mail\NewsletterManager properties: mailer: '@mailer' .. code-block:: xml + - + @@ -219,7 +227,8 @@ Another possibility is just setting public fields of the class directly:: .. code-block:: php - use AppBundle\Mail\NewsletterManager; + // config/services.php + use App\Mail\NewsletterManager; use Symfony\Component\DependencyInjection\Reference; // ... diff --git a/service_container/lazy_services.rst b/service_container/lazy_services.rst index da91903ac05..ec39857c6f7 100644 --- a/service_container/lazy_services.rst +++ b/service_container/lazy_services.rst @@ -51,14 +51,14 @@ You can mark the service as ``lazy`` by manipulating its definition: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\Twig\AppExtension: + App\Twig\AppExtension: lazy: true .. code-block:: xml - + - + .. code-block:: php - // app/config/services.php - use AppBundle\Twig\AppExtension; + // config/services.php + use App\Twig\AppExtension; $container->register(AppExtension::class) ->setLazy(true); diff --git a/service_container/optional_dependencies.rst b/service_container/optional_dependencies.rst index 3148bd30c47..bbd0e775745 100644 --- a/service_container/optional_dependencies.rst +++ b/service_container/optional_dependencies.rst @@ -15,7 +15,7 @@ if the service does not exist: .. code-block:: xml - + - + @@ -33,8 +33,8 @@ if the service does not exist: .. code-block:: php - // app/config/services.php - use AppBundle\Newsletter\NewsletterManager; + // config/services.php + use App\Newsletter\NewsletterManager; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -64,16 +64,16 @@ call if the service exists and remove the method call if it does not: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: app.newsletter_manager: - class: AppBundle\Newsletter\NewsletterManager + class: App\Newsletter\NewsletterManager calls: - [setLogger, ['@?logger']] .. code-block:: xml - + - + @@ -95,8 +95,8 @@ call if the service exists and remove the method call if it does not: .. code-block:: php - // app/config/services.php - use AppBundle\Newsletter\NewsletterManager; + // config/services.php + use App\Newsletter\NewsletterManager; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerInterface; diff --git a/service_container/parameters.rst b/service_container/parameters.rst index 44a7ed1e161..730a928f510 100644 --- a/service_container/parameters.rst +++ b/service_container/parameters.rst @@ -17,11 +17,13 @@ Use the ``parameters`` section of a config file to set parameters: .. code-block:: yaml + # config/services.yaml parameters: mailer.transport: sendmail .. code-block:: xml + setParameter('mailer.transport', 'sendmail'); You can refer to parameters elsewhere in any config file by surrounding them @@ -50,15 +53,17 @@ and hidden with the service definition: .. code-block:: yaml + # config/services.yaml parameters: mailer.transport: sendmail services: - AppBundle\Service\Mailer: + App\Service\Mailer: arguments: ['%mailer.transport%'] .. code-block:: xml + @@ -290,7 +296,7 @@ include as many services as needed in it. .. code-block:: php - // app/config/services.php + // config/services.php use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\Reference; @@ -298,13 +304,20 @@ include as many services as needed in it. $container ->register('app.command_handler_locator', ServiceLocator::class) - ->addTag('container.service_locator') ->setArguments(array(array( - 'AppBundle\FooCommand' => new Reference('app.command_handler.foo'), - 'AppBundle\BarCommand' => new Reference('app.command_handler.bar'), + 'App\FooCommand' => new Reference('app.command_handler.foo'), + 'App\BarCommand' => new Reference('app.command_handler.bar'), ))) + // if you are not using the default service autoconfiguration, + // add the following tag to the service definition: + // ->addTag('container.service_locator') ; +.. versionadded:: 4.1 + The service locator autoconfiguration was introduced in Symfony 4.1. In + previous Symfony versions you always needed to add the + ``container.service_locator`` tag explicitly. + .. note:: The services defined in the service locator argument must include keys, @@ -316,14 +329,14 @@ Now you can use the service locator by injecting it in any other service: .. code-block:: yaml - // app/config/services.yml + # config/services.yaml services: - AppBundle\CommandBus: + App\CommandBus: arguments: ['@app.command_handler_locator'] .. code-block:: xml - + - + @@ -340,8 +353,8 @@ Now you can use the service locator by injecting it in any other service: .. code-block:: php - // app/config/services.php - use AppBundle\CommandBus; + // config/services.php + use App\CommandBus; use Symfony\Component\DependencyInjection\Reference; $container diff --git a/service_container/shared.rst b/service_container/shared.rst index 7fec4267f67..decb9d09cf2 100644 --- a/service_container/shared.rst +++ b/service_container/shared.rst @@ -16,26 +16,26 @@ in your service definition: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\SomeNonSharedService: + App\SomeNonSharedService: shared: false # ... .. code-block:: xml - + - + .. code-block:: php - // app/config/services.php - use AppBundle\SomeNonSharedService; + // config/services.php + use App\SomeNonSharedService; $container->register(SomeNonSharedService::class) ->setShared(false); -Now, whenever you request the ``AppBundle\SomeNonSharedService`` from the container, +Now, whenever you request the ``App\SomeNonSharedService`` from the container, you will be passed a new instance. diff --git a/service_container/synthetic_services.rst b/service_container/synthetic_services.rst index ee550947af1..94d93fdbdc8 100644 --- a/service_container/synthetic_services.rst +++ b/service_container/synthetic_services.rst @@ -35,14 +35,15 @@ configuration: .. code-block:: yaml + # config/services.yaml services: - # synthetic services don't specify a class app.synthetic_service: synthetic: true .. code-block:: xml + register('app.synthetic_service') ->setSynthetic(true) diff --git a/service_container/tags.rst b/service_container/tags.rst index 1fa045a4be4..b2a4c038a9c 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -13,15 +13,15 @@ example: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\Twig\AppExtension: + App\Twig\AppExtension: public: false tags: [twig.extension] .. code-block:: xml - + - + @@ -37,8 +37,8 @@ example: .. code-block:: php - // app/config/services.php - use AppBundle\Twig\AppExtension; + // config/services.php + use App\Twig\AppExtension; $container->register(AppExtension::class) ->setPublic(false) @@ -58,9 +58,9 @@ learn how to create your own custom tags, keep reading. Autoconfiguring Tags -------------------- -Starting in Symfony 3.3, if you enable :ref:`autoconfigure `, -then some tags are automatically applied for you. That's true for the ``twig.extension`` -tag: the container sees that your class extends ``Twig_Extension`` (or more accurately, +If you enable :ref:`autoconfigure `, then some tags are +automatically applied for you. That's true for the ``twig.extension`` tag: the +container sees that your class extends ``Twig_Extension`` (or more accurately, that it implements ``Twig_ExtensionInterface``) and adds the tag for you. .. tip:: @@ -69,8 +69,8 @@ that it implements ``Twig_ExtensionInterface``) and adds the tag for you. interface, call the :method:`Symfony\\Component\\DependencyInjection\\ContainerBuilder::registerForAutoconfiguration` method in an :doc:`extension ` or from your kernel:: - // app/AppKernel.php - class AppKernel extends Kernel + // src/Kernel.php + class Kernel extends Kernel { // ... @@ -98,8 +98,8 @@ ways of transporting the message until one succeeds. To begin with, define the ``TransportChain`` class:: - // src/AppBundle/Mail/TransportChain.php - namespace AppBundle\Mail; + // src/Mail/TransportChain.php + namespace App\Mail; class TransportChain { @@ -122,11 +122,13 @@ Then, define the chain as a service: .. code-block:: yaml + # config/services.yaml services: - AppBundle\Mail\TransportChain: ~ + App\Mail\TransportChain: ~ .. code-block:: xml + - + .. code-block:: php - use AppBundle\Mail\TransportChain; + // config/services.php + use App\Mail\TransportChain; $container->autowire(TransportChain::class); @@ -155,6 +158,7 @@ For example, you may add the following transports as services: .. code-block:: yaml + # config/services.yaml services: Swift_SmtpTransport: arguments: ['%mailer_host%'] @@ -165,6 +169,7 @@ For example, you may add the following transports as services: .. code-block:: xml + register(\Swift_SmtpTransport::class) ->addArgument('%mailer_host%') ->addTag('app.mail_transport'); @@ -205,13 +211,13 @@ Create a Compiler Pass You can now use a :ref:`compiler pass ` to ask the container for any services with the ``app.mail_transport`` tag:: - // src/AppBundle/DependencyInjection/Compiler/MailTransportPass.php - namespace AppBundle\DependencyInjection\Compiler; + // src/DependencyInjection/Compiler/MailTransportPass.php + namespace App\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Reference; - use AppBundle\Mail\TransportChain; + use App\Mail\TransportChain; class MailTransportPass implements CompilerPassInterface { @@ -238,18 +244,21 @@ Register the Pass with the Container ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to run the compiler pass when the container is compiled, you have to -add the compiler pass to the container in the ``build()`` method of your -bundle:: +add the compiler pass to the container in a :doc:`bundle extension ` +or from your kernel:: - // src/AppBundle/AppBundle.php + // src/Kernel.php + namespace App; + use App\DependencyInjection\Compiler\MailTransportPass; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; // ... - use Symfony\Component\DependencyInjection\ContainerBuilder; - use AppBundle\DependencyInjection\Compiler\MailTransportPass; - class AppBundle extends Bundle + class Kernel extends BaseKernel { - public function build(ContainerBuilder $container) + // ... + + protected function build(ContainerBuilder $container) { $container->addCompilerPass(new MailTransportPass()); } @@ -303,6 +312,7 @@ To answer this, change the service declaration: .. code-block:: yaml + # config/services.yaml services: Swift_SmtpTransport: arguments: ['%mailer_host%'] @@ -315,6 +325,7 @@ To answer this, change the service declaration: .. code-block:: xml + register(\Swift_SmtpTransport::class) ->addArgument('%mailer_host%') ->addTag('app.mail_transport', array('alias' => 'foo')); @@ -351,8 +363,8 @@ To answer this, change the service declaration: .. code-block:: yaml + # config/services.yaml services: - # Compact syntax Swift_SendmailTransport: class: \Swift_SendmailTransport @@ -364,10 +376,6 @@ To answer this, change the service declaration: tags: - { name: app.mail_transport } - .. versionadded:: 3.3 - Support for the compact tag notation in the YAML format was introduced - in Symfony 3.3. - Notice that you've added a generic ``alias`` key to the tag. To actually use this, update the compiler:: @@ -402,10 +410,6 @@ tags set for the current service and gives you the attributes. Reference Tagged Services ~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 3.4 - Support for the tagged service notation in YAML, XML and PHP was introduced - in Symfony 3.4. - Symfony provides a shortcut to inject all services tagged with a specific tag, which is a common need in some applications, so you don't have to write a compiler pass just for that. @@ -417,21 +421,21 @@ first constructor argument to the ``App\HandlerCollection`` service: .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\Handler\One: + App\Handler\One: tags: [app.handler] - AppBundle\Handler\Two: + App\Handler\Two: tags: [app.handler] - AppBundle\HandlerCollection: + App\HandlerCollection: # inject all services tagged with app.handler as first argument arguments: [!tagged app.handler] .. code-block:: xml - + - + - + - + @@ -456,16 +460,16 @@ first constructor argument to the ``App\HandlerCollection`` service: .. code-block:: php - // app/config/services.php + // config/services.php use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; - $container->register(AppBundle\Handler\One::class) + $container->register(App\Handler\One::class) ->addTag('app.handler'); - $container->register(AppBundle\Handler\Two::class) + $container->register(App\Handler\Two::class) ->addTag('app.handler'); - $container->register(AppBundle\HandlerCollection::class) + $container->register(App\HandlerCollection::class) // inject all services tagged with app.handler as first argument ->addArgument(new TaggedIteratorArgument('app.handler')); @@ -474,8 +478,8 @@ application handlers. .. code-block:: php - // src/AppBundle/HandlerCollection.php - namespace AppBundle; + // src/HandlerCollection.php + namespace App; class HandlerCollection { @@ -492,15 +496,15 @@ application handlers. .. code-block:: yaml - # app/config/services.yml + # config/services.yaml services: - AppBundle\Handler\One: + App\Handler\One: tags: - { name: app.handler, priority: 20 } .. code-block:: xml - + - + @@ -516,8 +520,8 @@ application handlers. .. code-block:: php - // app/config/services.php - $container->register(AppBundle\Handler\One::class) + // config/services.php + $container->register(App\Handler\One::class) ->addTag('app.handler', array('priority' => 20)); Note that any other custom attributes will be ignored by this feature. diff --git a/session.rst b/session.rst index 144f068d34f..a046cc47896 100644 --- a/session.rst +++ b/session.rst @@ -1,8 +1,21 @@ Sessions ======== +Symfony provides a nice session object that you can use to store information +about the user between requests. + +To see how to use the session, read :ref:`session-intro`. + +More about Sessions +------------------- + .. toctree:: :maxdepth: 1 - :glob: - session/* + session/sessions_directory + session/avoid_session_start + session/locale_sticky_session + session/php_bridge + session/proxy_examples + +* :doc:`/doctrine/pdo_session_storage` diff --git a/session/avoid_session_start.rst b/session/avoid_session_start.rst index b5e8ab3f1c6..e19498184cf 100644 --- a/session/avoid_session_start.rst +++ b/session/avoid_session_start.rst @@ -36,7 +36,3 @@ access the flash messages: {% endfor %} {% endif %} - -.. versionadded:: 3.3 - The ``app.flashes()`` Twig function was introduced in Symfony 3.3. Prior, - you had to use ``app.session.flashBag()``. diff --git a/session/locale_sticky_session.rst b/session/locale_sticky_session.rst index f703c2edd37..e5233b183b0 100644 --- a/session/locale_sticky_session.rst +++ b/session/locale_sticky_session.rst @@ -13,12 +13,12 @@ in the session, so that it's used on subsequent requests. Creating a LocaleSubscriber --------------------------- -Create and a :ref:`new event subscriber `. Typically, ``_locale`` +Create a :ref:`new event subscriber `. Typically, ``_locale`` is used as a routing parameter to signify the locale, though you can determine the correct locale however you want:: - // src/AppBundle/EventSubscriber/LocaleSubscriber.php - namespace AppBundle\EventSubscriber; + // src/EventSubscriber/LocaleSubscriber.php + namespace App\EventSubscriber; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -58,7 +58,7 @@ correct locale however you want:: } } -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, you're done! Symfony will automatically know about the event subscriber and call the ``onKernelRequest`` method on each request. @@ -73,16 +73,18 @@ via some "Change Locale" route & controller), or create a route with a the :ref: .. code-block:: yaml + # config/services.yaml services: # ... - AppBundle\EventSubscriber\LocaleSubscriber: + App\EventSubscriber\LocaleSubscriber: arguments: ['%kernel.default_locale%'] - # redundant if you're using autoconfigure - tags: [kernel.event_subscriber] + # uncomment the next line if you are not using autoconfigure + # tags: [kernel.event_subscriber] .. code-block:: xml + - + %kernel.default_locale% - + + .. code-block:: php - use AppBundle\EventSubscriber\LocaleSubscriber; + // config/services.php + use App\EventSubscriber\LocaleSubscriber; $container->register(LocaleSubscriber::class) ->addArgument('%kernel.default_locale%') - ->addTag('kernel.event_subscriber'); + // uncomment the next line if you are not using autoconfigure + // ->addTag('kernel.event_subscriber'); That's it! Now celebrate by changing the user's locale and seeing that it's sticky throughout the request. @@ -115,7 +120,7 @@ method:: // from a controller... use Symfony\Component\HttpFoundation\Request; - public function indexAction(Request $request) + public function index(Request $request) { $locale = $request->getLocale(); } @@ -137,8 +142,8 @@ locale value before they are redirected to their first page. To do this, you need an event subscriber on the ``security.interactive_login`` event:: - // src/AppBundle/EventSubscriber/UserLocaleSubscriber.php - namespace AppBundle\EventSubscriber; + // src/EventSubscriber/UserLocaleSubscriber.php + namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -158,9 +163,6 @@ event:: $this->session = $session; } - /** - * @param InteractiveLoginEvent $event - */ public function onInteractiveLogin(InteractiveLoginEvent $event) { $user = $event->getAuthenticationToken()->getUser(); @@ -178,9 +180,9 @@ event:: } } -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, you're done! Symfony will automatically know about the event subscriber will pass -your the ``session`` service. Now, when you login, the user's locale will be set +you the ``session`` service. Now, when you login, the user's locale will be set into the session. .. caution:: diff --git a/session/php_bridge.rst b/session/php_bridge.rst index 3ebc74461ae..c1b5b78c742 100644 --- a/session/php_bridge.rst +++ b/session/php_bridge.rst @@ -15,6 +15,7 @@ for the ``handler_id``: .. code-block:: yaml + # config/packages/framework.yaml framework: session: storage_id: session.storage.php_bridge @@ -22,6 +23,7 @@ for the ``handler_id``: .. code-block:: xml + loadFromExtension('framework', array( 'session' => array( 'storage_id' => 'session.storage.php_bridge', @@ -54,6 +57,7 @@ the example below: .. code-block:: yaml + # config/packages/framework.yaml framework: session: storage_id: session.storage.php_bridge @@ -61,6 +65,7 @@ the example below: .. code-block:: xml + loadFromExtension('framework', array( 'session' => array( 'storage_id' => 'session.storage.php_bridge', diff --git a/session/proxy_examples.rst b/session/proxy_examples.rst index 4de2dda47cb..df7410c203a 100644 --- a/session/proxy_examples.rst +++ b/session/proxy_examples.rst @@ -11,7 +11,7 @@ a custom save handler just by defining a class that extends the class. Then, define the class as a :ref:`service `. -If you're using the :ref:`default services.yml configuration `, +If you're using the :ref:`default services.yaml configuration `, that happens automatically. Finally, use the ``framework.session.handler_id`` configuration option to tell @@ -21,15 +21,15 @@ Symfony to use your session handler instead of the default one: .. code-block:: yaml - # app/config/config.yml + # config/packages/framework.yaml framework: session: # ... - handler_id: AppBundle\Session\CustomSessionHandler + handler_id: App\Session\CustomSessionHandler .. code-block:: xml - + - + .. code-block:: php - // app/config/config.php - use AppBundle\Session\CustomSessionHandler; + // config/packages/framework.php + use App\Session\CustomSessionHandler; $container->loadFromExtension('framework', array( // ... 'session' => array( @@ -65,8 +65,8 @@ If you want to encrypt the session data, you can use the proxy to encrypt and decrypt the session as required. The following example uses the `php-encryption`_ library, but you can adapt it to any other library that you may be using:: - // src/AppBundle/Session/EncryptedSessionProxy.php - namespace AppBundle\Session; + // src/Session/EncryptedSessionProxy.php + namespace App\Session; use Defuse\Crypto\Crypto; use Defuse\Crypto\Key; @@ -105,10 +105,10 @@ There are some applications where a session is required for guest users, but where there is no particular need to persist the session. In this case you can intercept the session before it is written:: - // src/AppBundle/Session/ReadOnlySessionProxy.php - namespace AppBundle\Session; + // src/Session/ReadOnlySessionProxy.php + namespace App\Session; - use AppBundle\Entity\User; + use App\Entity\User; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; diff --git a/session/sessions_directory.rst b/session/sessions_directory.rst index c1b91f5472f..9e8d06ea627 100644 --- a/session/sessions_directory.rst +++ b/session/sessions_directory.rst @@ -11,7 +11,7 @@ this path, update the ``framework.session.save_path`` configuration key: .. code-block:: yaml - # app/config/config.yml + # config/packages/framework.yaml framework: session: handler_id: session.handler.native_file @@ -19,7 +19,7 @@ this path, update the ``framework.session.save_path`` configuration key: .. code-block:: xml - + loadFromExtension('framework', array( 'session' => array( 'handler_id' => 'session.handler.native_file', diff --git a/setup.rst b/setup.rst index 39f90b6f6c9..be557e48bea 100644 --- a/setup.rst +++ b/setup.rst @@ -4,297 +4,141 @@ Installing & Setting up the Symfony Framework ============================================= -This article explains how to install Symfony in different ways and how to solve -the most common issues that may appear during the installation process. - .. seealso:: - Do you prefer video tutorials? Check out the `Joyful Development with Symfony`_ + Do you prefer video tutorials? Check out the `Stellar Development with Symfony`_ screencast series from KnpUniversity. -Creating Symfony Applications ------------------------------ +To create your new Symfony application, first make sure you're using PHP 7.1 or higher +and have `Composer`_ installed. If you don't, start by :doc:`installing Composer globally ` +on your system. If you want to use a virtual machine (VM), check out :doc:`Homestead `. -Symfony provides a dedicated application called the **Symfony Installer** to ease -the creation of Symfony applications. This installer is a PHP 5.4 compatible -executable that needs to be installed on your system only once: +Create your new project by running: .. code-block:: terminal - # Linux and macOS systems - $ sudo mkdir -p /usr/local/bin - $ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony - $ sudo chmod a+x /usr/local/bin/symfony + $ composer create-project symfony/website-skeleton:^3.4 my-project - # Windows systems - c:\> php -r "file_put_contents('symfony', file_get_contents('https://symfony.com/installer'));" +This will create a new ``my-project`` directory, download some dependencies into +it and even generate the basic directories and files you'll need to get started. +In other words, your new app is ready! -.. note:: +.. tip:: - In Linux and macOS, a global ``symfony`` command is created. In Windows, - move the ``symfony`` file to a directory that's included in the ``PATH`` - environment variable and create a ``symfony.bat`` file to create the global - command or move it to any other directory convenient for you: + The ``website-skeleton`` is optimized for traditional web applications. If + you are building microservices, console applications or APIs, consider + using the much simpler ``skeleton`` project: .. code-block:: terminal - # for example, if WAMP is used ... - c:\> move symfony c:\wamp\bin\php - # create symfony.bat in the same folder - c:\> cd c:\wamp\bin\php - c:\> (echo @ECHO OFF & echo php "%~dp0symfony" %*) > symfony.bat - # ... then, execute the command as: - c:\> symfony - - # moving it to your projects folder ... - c:\> move symfony c:\projects - # ... then, execute the command as - c:\> cd projects - c:\projects\> php symfony - -.. _installation-creating-the-app: - -Once the Symfony Installer is installed, create your first Symfony application -with the ``new`` command: - -.. code-block:: terminal - - $ symfony new my_project_name - -This command creates a new directory called ``my_project_name/`` that contains -an empty project based on the most recent stable Symfony version available. In -addition, the installer checks if your system meets the technical requirements -to execute Symfony applications. If not, you'll see the list of changes needed -to meet those requirements. - -.. note:: - - If the installer doesn't work for you or doesn't output anything, make sure - that the PHP `Phar extension`_ is installed and enabled on your computer. - -.. note:: - - If the SSL certificates are not properly installed in your system, you - may get this error: - - cURL error 60: SSL certificate problem: unable to get local issuer certificate. - - You can solve this issue as follows: - - #. Download a file with the updated list of certificates from - https://curl.haxx.se/ca/cacert.pem - #. Move the downloaded ``cacert.pem`` file to some safe location in your system - #. Update your ``php.ini`` file and configure the path to that file: - - .. code-block:: ini + $ composer create-project symfony/skeleton my-project - ; Linux and macOS systems - curl.cainfo = "/path/to/cacert.pem" - - ; Windows systems - curl.cainfo = "C:\path\to\cacert.pem" - -Basing your Project on a Specific Symfony Version -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In case your project needs to be based on a specific Symfony version, use the -optional second argument of the ``new`` command: - -.. code-block:: terminal - - # use the most recent version in any Symfony branch - $ symfony new my_project_name 2.8 - $ symfony new my_project_name 3.1 - - # use a specific Symfony version - $ symfony new my_project_name 2.8.3 - $ symfony new my_project_name 3.1.5 - - # use a beta or RC version (useful for testing new Symfony versions) - $ symfony new my_project 2.7.0-BETA1 - $ symfony new my_project 2.7.0-RC1 - - # use the most recent 'lts' version (Long Term Support version) - $ symfony new my_project_name lts - -Each version has its *own* documentation, which you can select on any documentation -page. - -.. note:: - - Read the :doc:`Symfony Release process ` - to better understand why there are several Symfony versions and which one - to use for your projects. - -Creating Symfony Applications with Composer -------------------------------------------- - -If you can't use the Symfony installer for any reason, you can create Symfony -applications with `Composer`_, the dependency manager used by modern PHP -applications. - -If you don't have Composer installed in your computer, start by -:doc:`installing Composer globally `. Then, execute the -``create-project`` command to create a new Symfony application based on its -latest stable version: - -.. code-block:: terminal +Running your Symfony Application +-------------------------------- - $ composer create-project symfony/framework-standard-edition my_project_name +On production, you should use a web server like Nginx or Apache +(see :doc:`configuring a web server to run Symfony `). +But for development, it's even easier to use the :doc:`Symfony PHP web server `. -You can also install any other Symfony version by passing a second argument to -the ``create-project`` command: +First, move into your new project and install the server: .. code-block:: terminal - $ composer create-project symfony/framework-standard-edition my_project_name "2.8.*" + $ cd my-project + $ composer require server --dev -.. tip:: - - If your Internet connection is slow, you may think that Composer is not - doing anything. If that's your case, add the ``-vvv`` flag to the previous - command to display a detailed output of everything that Composer is doing. - -Running the Symfony Application -------------------------------- - -On production servers, Symfony applications use web servers such as Apache or -Nginx (see :doc:`configuring a web server to run Symfony `). -However, on your local development machine you can also use the web server -provided by Symfony, which in turn uses the built-in web server provided by PHP. - -First, :doc:`install the Symfony Web Server ` and -then, execute this command: +To start the server, run: .. code-block:: terminal - $ cd my_project_name/ $ php bin/console server:run -Open your browser and access the ``http://localhost:8000/`` URL to see the -Welcome Page of Symfony: +Open your browser and navigate to ``http://localhost:8000/``. If everything is working, +you'll see a welcome page. Later, when you are finished working, stop the server +by pressing ``Ctrl+C`` from your terminal. -.. image:: /_images/quick_tour/welcome.png - :align: center - :alt: Symfony Welcome Page - :class: with-browser - -If you see a blank page or an error page instead of the Welcome Page, there is -a directory permission misconfiguration. The solution to this problem is -explained in the :doc:`/setup/file_permissions`. +.. tip:: -When you are finished working on your Symfony application, stop the server by -pressing ``Ctrl+C`` from the terminal or command console. + If you're having any problems running Symfony, your system may be missing + some technical requirements. Use the :doc:`Symfony Requirements Checker ` + tool to make sure your system is set up. .. tip:: - Symfony's web server is great for developing, but should **not** be - used on production. Instead, use Apache or Nginx. - See :doc:`/setup/web_server_configuration`. + If you're using a VM, you may need to tell the server to bind to all IP addresses: -Checking Symfony Application Configuration and Setup ----------------------------------------------------- - -The Symfony Installer checks if your system is ready to run Symfony applications. -However, the PHP configuration for the command console can be different from the -PHP web configuration. For that reason, Symfony provides a visual configuration -checker. Access the following URL to check your configuration and fix any issue -before moving on: + .. code-block:: terminal -.. code-block:: text + $ php bin/console server:start 0.0.0.0:8000 - http://localhost:8000/config.php + You should **NEVER** listen to all interfaces on a computer that is + directly accessible from the Internet. -Fixing Permissions Problems +Storing your Project in git --------------------------- -If you have any file permission errors or see a white screen, then read -:doc:`/setup/file_permissions` for more information. - -.. _installation-updating-vendors: - -Updating Symfony Applications ------------------------------ - -At this point, you've created a fully-functional Symfony application! Every Symfony -app depends on a number of third-party libraries stored in the ``vendor/`` directory -and managed by Composer. - -Updating those libraries frequently is a good practice to prevent bugs and -security vulnerabilities. Execute the ``update`` Composer command to update them -all at once (this can take up to several minutes to complete depending on the -complexity of your project): +Storing your project in services like GitHub, GitLab and Bitbucket is easy! New +Symfony projects include an empty Git repository, so you can add everything and +commit: .. code-block:: terminal - $ cd my_project_name/ - $ composer update - -.. tip:: - - Symfony provides a command to check whether your project's dependencies - contain any known security vulnerability: - - .. code-block:: terminal + $ git add . + $ git commit -m "Initial commit" - $ php bin/console security:check +Your project already has a sensible ``.gitignore`` file. And as you install more +packages, a system called :ref:`Flex ` will add more lines to +that file when needed. - A good security practice is to execute this command regularly to be able to - update or replace compromised dependencies as soon as possible. +.. _install-existing-app: -.. _installing-a-symfony2-distribution: +Setting up an Existing Symfony Project +-------------------------------------- -Installing the Symfony Demo or Other Distributions --------------------------------------------------- +If you're working on an existing Symfony application, you'll just need to do a few +things to get your project setup. Assuming your team uses Git, you can setup your +project with the following commands: -You've already downloaded the `Symfony Standard Edition`_: the default starting project -for all Symfony apps. You'll use this project throughout the documentation to build -your app! +.. code-block:: terminal -Symfony also provides some other projects and starting skeletons that you can use: + # clone the project to download its contents + $ cd projects/ + $ git clone ... -`The Symfony Demo Application`_ - This is a fully-functional application that shows the recommended way to develop - Symfony applications. The app has been conceived as a learning tool for Symfony - newcomers and its source code contains tons of comments and helpful notes. + # make Composer install the project's dependencies into vendor/ + $ cd my-project/ + $ composer install -`The Symfony CMF Standard Edition`_ - The `Symfony CMF`_ is a project that helps make it easier for developers to add - CMS functionality to their Symfony applications. This is a starting project - containing the Symfony CMF. +You'll probably also need to customize your :ref:`.env ` and do a +few other project-specific tasks (e.g. creating database schema). -`The Symfony REST Edition`_ - Shows how to build an application that provides a RESTful API using the - `FOSRestBundle`_ and several other related Bundles. +Checking for Security Vulnerabilities +------------------------------------- -.. _install-existing-app: +Symfony provides a utility called the "Security Checker" (or ``sec-checker``) to +check whether your project's dependencies contain any known security +vulnerability. Run this command to install it in your application: -Installing an Existing Symfony Application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When working collaboratively in a Symfony application, it's uncommon to create -a new Symfony application as explained in the previous sections. Instead, -someone else has already created and submitted it to a shared repository. +.. code-block:: terminal -It's recommended to not submit some files (:ref:`parameters.yml `) -and directories (``vendor/``, cache, logs) to the repository, so you'll have to do -the following when installing an existing Symfony application: + $ cd my-project/ + $ composer require sec-checker --dev -.. code-block:: terminal +From now on, this utility will be run automatically whenever you install or +update any dependency in the application. If a dependency contains a vulnerability, +you'll see a clear message. - # clone the project to download its contents - $ cd projects/ - $ git clone ... +The Symfony Demo application +---------------------------- - # make Composer install the project's dependencies into vendor/ - $ cd my_project_name/ - $ composer install +`The Symfony Demo Application`_ is a fully-functional application that shows the +recommended way to develop Symfony applications. It's a great learning tool for +Symfony newcomers and its code contains tons of comments and helpful notes. - # now Composer will ask you for the values of any undefined parameter - $ ... +To check out its code and install it locally, see `symfony/symfony-demo`_. -Keep Going! ------------ +Start Coding! +------------- With setup behind you, it's time to :doc:`Create your first page in Symfony `. @@ -311,18 +155,13 @@ Go Deeper with Setup :glob: setup/homestead - setup/new_project_git setup/built_in_web_server setup/web_server_configuration setup/composer setup/* -.. _`Joyful Development with Symfony`: http://knpuniversity.com/screencast/symfony +.. _`Stellar Development with Symfony`: http://knpuniversity.com/screencast/symfony .. _`Composer`: https://getcomposer.org/ -.. _`Phar extension`: https://php.net/manual/en/intro.phar.php -.. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard +.. _`technical requirements`: https://symfony.com/doc/current/reference/requirements.html .. _`The Symfony Demo application`: https://github.com/symfony/symfony-demo -.. _`The Symfony CMF Standard Edition`: https://github.com/symfony-cmf/standard-edition -.. _`Symfony CMF`: http://cmf.symfony.com/ -.. _`The Symfony REST Edition`: https://github.com/gimler/symfony-rest-edition -.. _`FOSRestBundle`: https://github.com/FriendsOfSymfony/FOSRestBundle +.. _`symfony/symfony-demo`: https://github.com/symfony/demo diff --git a/setup/_vendor_deps.rst.inc b/setup/_vendor_deps.rst.inc index bbb4ec1f20b..420c9b2081b 100644 --- a/setup/_vendor_deps.rst.inc +++ b/setup/_vendor_deps.rst.inc @@ -11,8 +11,7 @@ you need for each. By default, these libraries are downloaded by running a ``composer install`` "downloader" binary. This ``composer`` file is from a library called `Composer`_ -and you can read more about installing it in the :ref:`Installation ` -article. +and you can read more about :doc:`installing Composer globally `. The ``composer`` command reads from the ``composer.json`` file at the root of your project. This is an JSON-formatted file, which holds a list of each diff --git a/setup/built_in_web_server.rst b/setup/built_in_web_server.rst index e6d94fbc2dd..37626e7cc04 100644 --- a/setup/built_in_web_server.rst +++ b/setup/built_in_web_server.rst @@ -22,34 +22,12 @@ and enable the server bundle. Installing the Web Server Bundle -------------------------------- -First, execute this command: +Move into your project directory and run this command: .. code-block:: terminal $ cd your-project/ - $ composer require --dev symfony/web-server-bundle - -Then, enable the bundle in the kernel of the application:: - - // app/AppKernel.php - class AppKernel extends Kernel - { - public function registerBundles() - { - $bundles = array( - // ... - ); - - if ('dev' === $this->getEnvironment()) { - // ... - $bundles[] = new Symfony\Bundle\WebServerBundle\WebServerBundle(); - } - - // ... - } - - // ... - } + $ composer require server --dev Starting the Web Server ----------------------- @@ -75,9 +53,6 @@ can change the socket passing an IP address and a port as a command-line argumen # passing '*' as the IP means to use 0.0.0.0 (i.e. any local IP address) $ php bin/console server:start *:8080 -.. versionadded:: 3.4 - The support of ``*`` as a valid IP address was introduced in Symfony 3.4. - .. note:: You can use the ``server:status`` command to check if a web server is @@ -127,7 +102,7 @@ Use the ``--router`` option to use your own router script: .. code-block:: terminal - $ php bin/console server:start --router=app/config/my_router.php + $ php bin/console server:start --router=config/my_router.php If your application's document root differs from the standard directory layout, you have to pass the correct location using the ``--docroot`` option: diff --git a/setup/bundles.rst b/setup/bundles.rst index ba346ecc7fe..a0af605d446 100644 --- a/setup/bundles.rst +++ b/setup/bundles.rst @@ -69,7 +69,8 @@ PHPUnit test report: .. code-block:: terminal - $ phpunit + # this command is available after running "composer require --dev symfony/phpunit-bridge" + $ ./bin/phpunit # ... PHPUnit output diff --git a/setup/file_permissions.rst b/setup/file_permissions.rst index cb21d44210c..5c8593313a5 100644 --- a/setup/file_permissions.rst +++ b/setup/file_permissions.rst @@ -1,86 +1,19 @@ Setting up or Fixing File Permissions ===================================== -One important Symfony requirement is that the ``var`` directory must be -writable both by the web server and the command line user. +In Symfony 3.x, you needed to do some extra work to make sure that your cache directory +was writable. But that is no longer true! In Symfony 4, everything works automatically: -On Linux and macOS systems, if your web server user is different from your -command line user, you need to configure permissions properly to avoid issues. -There are several ways to achieve that: +* In the ``dev`` environment, ``umask()`` is used in ``bin/console`` and ``public/index.php`` + so that any created files are writable by everyone. -1. Use the same User for the CLI and the Web Server -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Edit your web server configuration (commonly ``httpd.conf`` or ``apache2.conf`` -for Apache) and set its user to be the same as your CLI user (e.g. for Apache, -update the ``User`` and ``Group`` directives). - -.. caution:: - - If this solution is used in a production server, be sure this user only has - limited privileges (no access to private data or servers, execution of - unsafe binaries, etc.) as a compromised server would give to the hacker - those privileges. - -2. Using ACL on a System that Supports ``chmod +a`` (macOS) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -On macOS systems, the ``chmod`` command supports the ``+a`` flag to define an -ACL. Use the following script to determine your web server user and grant the -needed permissions: - -.. code-block:: terminal - - $ rm -rf var/cache/* - $ rm -rf var/logs/* - - $ HTTPDUSER=$(ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1) - $ sudo chmod +a "$HTTPDUSER allow delete,write,append,file_inherit,directory_inherit" var - $ sudo chmod +a "$(whoami) allow delete,write,append,file_inherit,directory_inherit" var - -3. Using ACL on a System that Supports ``setfacl`` (Linux/BSD) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Most Linux and BSD distributions don't support ``chmod +a``, but do support -another utility called ``setfacl``. You may need to install ``setfacl`` and -`enable ACL support`_ on your disk partition before using it. Then, use the -following script to determine your web server user and grant the needed permissions: - -.. code-block:: terminal - - $ HTTPDUSER=$(ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1) - # if this doesn't work, try adding `-n` option - $ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var - $ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:$(whoami):rwX var +* In the ``prod`` environment (i.e. when ``APP_ENV`` is ``prod`` and ``APP_DEBUG`` + is ``0``), as long as you run ``php bin/console cache:warmup``, no cache files + will need to be written to disk at runtime. .. note:: -   The first ``setfacl`` command sets permissions for future files and folders, - while the second one sets permissions on the existing files and folders. - Both of these commands assign permissions for the system user and the Apache - user. - - ``setfacl`` isn't available on NFS mount points. However, storing cache and - logs over NFS is strongly discouraged for performance reasons. - -4. Without Using ACL -~~~~~~~~~~~~~~~~~~~~ - -If none of the previous methods work for you, change the umask so that the -cache and log directories are group-writable or world-writable (depending -if the web server user and the command line user are in the same group or not). -To achieve this, put the following line at the beginning of the ``bin/console``, -``web/app.php`` and ``web/app_dev.php`` files:: - - umask(0002); // This will let the permissions be 0775 - - // or - - umask(0000); // This will let the permissions be 0777 - -.. note:: - - Changing the umask is not thread-safe, so the ACL methods are recommended - when they are available. - -.. _`enable ACL support`: https://help.ubuntu.com/community/FilePermissionsACLs + If you decide to store log files on disk, you *will* need to make sure your + logs directory (e.g. ``var/log/``) is writable by your web server user and + terminal user. One way this can be done is by using ``chmod -R 777 var/log/``. + Just be aware that your logs are readable by any user on your production system. diff --git a/setup/flex.rst b/setup/flex.rst index da805b9f6e7..d0dad01f4fb 100644 --- a/setup/flex.rst +++ b/setup/flex.rst @@ -124,10 +124,12 @@ Symfony application by executing the following command: recommended since Symfony 3.3. Use the Composer ``create-project`` command instead. +.. _upgrade-to-flex: + Upgrading Existing Applications to Flex --------------------------------------- -Using Symfony Flex is optional, even in Symfony 4, where Flex will be used by +Using Symfony Flex is optional, even in Symfony 4, where Flex is used by default. However, Flex is so convenient and improves your productivity so much that it's strongly recommended to upgrade your existing applications to it. @@ -137,6 +139,9 @@ following directory structure, which is the same used by default in Symfony 4: .. code-block:: text your-project/ + ├── assets/ + ├── bin/ + │   └── console ├── config/ │   ├── bundles.php │   ├── packages/ @@ -148,6 +153,9 @@ following directory structure, which is the same used by default in Symfony 4: │   ├── ... │   └── Kernel.php ├── templates/ + ├── tests/ + ├── translations/ + ├── var/ └── vendor/ This means that installing the ``symfony/flex`` dependency in your application @@ -155,37 +163,115 @@ is not enough. You must also upgrade the directory structure to the one shown above. There's no automatic tool to make this upgrade, so you must follow these manual steps: -#. Create a new empty Symfony application (``composer create-project - symfony/skeleton my-project-flex``) +#. Install Flex as a dependency of your project: + + .. code-block:: terminal + + $ composer require symfony/flex + +#. If the project's ``composer.json`` file contains ``symfony/symfony`` dependency, + it still depends on the Symfony Standard edition, which is no longer available + in Symfony 4. First, remove this dependency: + + .. code-block:: terminal + + $ composer remove symfony/symfony + + Now add the ``symfony/symfony`` package to the ``conflict`` section of the project's + ``composer.json`` file as `shown in this example of the skeleton-project`_ so that + it will not be installed again: + + .. code-block:: diff + + { + "require": { + "symfony/flex": "^1.0", + + }, + + "conflict": { + + "symfony/symfony": "*" + } + } + + Now you must add in ``composer.json`` all the Symfony dependencies required + by your project. A quick way to do that is to add all the components that + were included in the previous ``symfony/symfony`` dependency and later you + can remove anything you don't really need: + + .. code-block:: terminal + + $ composer require annotations asset orm-pack twig \ + logger mailer form security translation validator + $ composer require --dev dotenv maker-bundle orm-fixtures profiler + +#. If the project's ``composer.json`` file doesn't contain ``symfony/symfony`` + dependency, it already defines its dependencies explicitly, as required by + Flex. You just need to reinstall all dependencies to force Flex generate the + config files in ``config/``, which is the most tedious part of the upgrade + process: + + .. code-block:: terminal + + $ rm -fr vendor/* + $ composer install + +#. No matter which of the previous steps you followed. At this point, you'll have + lots of new config files in ``config/``. They contain the default config + defined by Symfony, so you must check your original files in ``app/config/`` + and make the needed changes in the new files. Flex config doesn't use suffixes + in config files, so the old ``app/config/config_dev.yml`` goes to + ``config/packages/dev/*.yaml``, etc. + +#. The most important config file is ``app/config/services.yml``, which now is + located at ``config/services.yaml``. Copy the contents of the + `default services.yaml file`_ and then add your own service configuration. + Later you can revisit this file because thanks to Symfony's + :doc:`autowiring feature ` you can remove + most of the service configuration. + + .. note:: + + Make sure that your previous configuration files don't have ``imports`` + declarations pointing to resources already loaded by ``Kernel::configureContainer()`` + or ``Kernel::configureRoutes()`` methods. + +#. Move the rest of the ``app/`` contents as follows (and after that, remove the + ``app/`` directory): + + * ``app/Resources/views/`` -> ``templates/`` + * ``app/Resources/translations/`` -> ``translations/`` + * ``app/Resources//views/`` -> ``templates/bundles//`` + * rest of ``app/Resources/`` files -> ``src/Resources/`` + +#. Move the original PHP source code from ``src/AppBundle/*``, except bundle + specific files (like ``AppBundle.php`` and ``DependencyInjection/``), to + ``src/``. Remove ``src/AppBundle/``. -#. Merge the ``require`` and ``require-dev`` dependencies defined in your - original project's ``composer.json`` file to the ``composer.json`` file of the - new project (don't copy the ``symfony/symfony`` dependency, but add the - relevant components you are effectively using in your project). + In addition to moving the files, update the ``autoload`` and ``autoload-dev`` + values of the ``composer.json`` file as `shown in this example`_ to use + ``App\`` and ``App\Tests\`` as the application namespaces (advanced IDEs can + do this automatically). -#. Install the dependencies in the new project executing ``composer update``. - This will make Symfony Flex generate all the configuration files in - ``config/packages/`` + If you used multiple bundles to organize your code, you must reorganize your + code into ``src/``. For example, if you had ``src/UserBundle/Controller/DefaultController.php`` + and ``src/ProductBundle/Controller/DefaultController.php``, you could move + them to ``src/Controller/UserController.php`` and ``src/Controller/ProductController.php``. -#. Review the generated ``config/packages/*.yaml`` files and make any needed - changes according to the configuration defined in the - ``app/config/config_*.yml`` file of your original project. Beware that this is - the most time-consuming and error-prone step of the upgrade process. +#. Move the public assets, such as images or compiled CSS/JS files, from + ``src/AppBundle/Resources/public/`` to ``public/`` (e.g. ``public/images/``). -#. Move the original parameters defined in ``app/config/parameters.*.yml`` to - the new ``config/services.yaml`` and ``.env`` files depending on your needs. +#. Move the source of the assets (e.g. the SCSS files) to ``assets/`` and use + :doc:`Webpack Encore ` to manage and compile them. -#. Move the original source code from ``src/{App,...}Bundle/`` to ``src/`` and - update the namespaces of every PHP file to be ``App\...`` (advanced IDEs can do - this automatically). +#. Create the new ``public/index.php`` front controller + `copying Symfony's index.php source`_ and, if you made any customization in + your ``web/app.php`` and ``web/app_dev.php`` files, copy those changes into + the new file. You can now remove the old ``web/`` dir. -#. Move the original templates from ``app/Resources/views/`` to ``templates/`` - and ``app/Resources/translations`` to ``translations/``. There may be a few - other files you need to move into a new location. +#. Update the ``bin/console`` script `copying Symfony's bin/console source`_ + and changing anything according to your original console script. -#. Make any other change needed by your application. For example, if your - original ``web/app_*.php`` front controllers were customized, add those changes - to the new ``public/index.php`` controller. +#. Remove the ``bin/symfony_requirements`` script and if you need a replacement + for it, use the new `Symfony Requirements Checker`_. .. _`Symfony Flex`: https://github.com/symfony/flex .. _`Symfony Installer`: https://github.com/symfony/symfony-installer @@ -193,3 +279,9 @@ manual steps: .. _`Main recipe repository`: https://github.com/symfony/recipes .. _`Contrib recipe repository`: https://github.com/symfony/recipes-contrib .. _`Symfony Recipes documentation`: https://github.com/symfony/recipes/blob/master/README.rst +.. _`default services.yaml file`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.3/config/services.yaml +.. _`shown in this example`: https://github.com/symfony/skeleton/blob/8e33fe617629f283a12bbe0a6578bd6e6af417af/composer.json#L24-L33 +.. _`shown in this example of the skeleton-project`: https://github.com/symfony/skeleton/blob/8e33fe617629f283a12bbe0a6578bd6e6af417af/composer.json#L44-L46 +.. _`copying Symfony's index.php source`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.3/public/index.php +.. _`copying Symfony's bin/console source`: https://github.com/symfony/recipes/blob/master/symfony/console/3.3/bin/console +.. _`Symfony Requirements Checker`: https://github.com/symfony/requirements-checker diff --git a/setup/homestead.rst b/setup/homestead.rst index 5c7e0fd5fe8..e28e11f8418 100644 --- a/setup/homestead.rst +++ b/setup/homestead.rst @@ -49,10 +49,13 @@ configuration: # ... sites: - map: symfony-demo.test - to: /home/vagrant/projects/symfony_demo/web - type: symfony + to: /home/vagrant/projects/symfony_demo/public + type: symfony4 The ``type`` option tells Homestead to use the Symfony nginx configuration. +Homestead now supports a Symfony 2 and 3 web layout with ``app.php`` and +``app_dev.php`` when using type ``symfony2`` and an ``index.php`` layout when +using type ``symfony4``. At last, edit the hosts file on your local machine to map ``symfony-demo.test`` to ``192.168.10.10`` (which is the IP used by Homestead):: diff --git a/setup/new_project_git.rst b/setup/new_project_git.rst deleted file mode 100644 index 6c175456c05..00000000000 --- a/setup/new_project_git.rst +++ /dev/null @@ -1,102 +0,0 @@ -.. index:: - single: Set Up; Git - -.. _how-to-create-and-store-a-symfony2-project-in-git: - -How to Create and Store a Symfony Project in Git -================================================ - -.. tip:: - - Though this entry is specifically about Git, the same generic principles - will apply if you're storing your project in Subversion. - -Once you've read through :doc:`/page_creation` and become familiar with -using Symfony, you'll no-doubt be ready to start your own project. In this -article, you'll learn the best way to start a new Symfony project that's stored -using the `Git`_ source control management system. - -Initial Project Setup ---------------------- - -To get started, you'll need to download Symfony and get things running. See -the :doc:`/setup` article for details. - -Once your project is running, just follow these simple steps: - -#. Initialize your Git repository: - - .. code-block:: terminal - - $ git init - -#. Add all of the initial files to Git: - - .. code-block:: terminal - - $ git add . - - .. tip:: - - As you might have noticed, not all files that were downloaded by Composer in step 1, - have been staged for commit by Git. Certain files and folders, such as the project's - dependencies (which are managed by Composer), ``parameters.yml`` (which contains sensitive - information such as database credentials), log and cache files and dumped assets (which are - created automatically by your project), should not be committed in Git. To help you prevent - committing those files and folders by accident, the Standard Distribution comes with a - file called ``.gitignore``, which contains a list of files and folders that Git should - ignore. - - .. tip:: - - You may also want to create a ``.gitignore`` file that can be used system-wide. - This allows you to exclude files/folders for all your projects that are created by - your IDE or operating system. For details, see `GitHub .gitignore`_. - -#. Create an initial commit with your started project: - - .. code-block:: terminal - - $ git commit -m "Initial commit" - -At this point, you have a fully-functional Symfony project that's correctly -committed to Git. You can immediately begin development, committing the new -changes to your Git repository. - -You can continue to follow along with the :doc:`/page_creation` article -to learn more about how to configure and develop inside your application. - -.. tip:: - - The Symfony Standard Edition comes with some example functionality. To - remove the sample code, follow the instructions in the - ":doc:`/bundles/remove`" article. - -.. include:: _vendor_deps.rst.inc - -Storing your Project on a remote Server ---------------------------------------- - -You now have a fully-functional Symfony project stored in Git. However, -in most cases, you'll also want to store your project on a remote server -both for backup purposes, and so that other developers can collaborate on -the project. - -The easiest way to store your project on a remote server is via a web-based -hosting service like `GitHub`_ or `Bitbucket`_. Of course, there are more -services out there, you can start your research with a -`comparison of hosting services`_. - -Alternatively, you can store your Git repository on any server by creating -a `barebones repository`_ and then pushing to it. One library that helps -manage this is `Gitolite`_. - -.. _`Git`: https://git-scm.com/ -.. _`Symfony Standard Edition`: https://symfony.com/download -.. _`git submodules`: https://git-scm.com/book/en/Git-Tools-Submodules -.. _`GitHub`: https://github.com/ -.. _`barebones repository`: https://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository -.. _`Gitolite`: https://github.com/sitaramc/gitolite -.. _`GitHub .gitignore`: https://help.github.com/articles/ignoring-files -.. _`Bitbucket`: https://bitbucket.org/ -.. _`comparison of hosting services`: https://en.wikipedia.org/wiki/Comparison_of_open-source_software_hosting_facilities diff --git a/setup/new_project_svn.rst b/setup/new_project_svn.rst deleted file mode 100644 index dcfac5bfcbf..00000000000 --- a/setup/new_project_svn.rst +++ /dev/null @@ -1,144 +0,0 @@ -.. index:: - single: Set Up; Subversion - -.. _how-to-create-and-store-a-symfony2-project-in-subversion: - -How to Create and Store a Symfony Project in Subversion -======================================================= - -.. tip:: - - This entry is specifically about Subversion, and based on principles found - in :doc:`/setup/new_project_git`. - -Once you've read through :doc:`/page_creation` and become familiar with -using Symfony, you'll no-doubt be ready to start your own project. The -preferred method to manage Symfony projects is using `Git`_ but some prefer -to use `Subversion`_ which is totally fine!. In this article, you'll learn how -to manage your project using `SVN`_ in a similar manner you would do with -`Git`_. - -.. tip:: - - This is **a** method to tracking your Symfony project in a Subversion - repository. There are several ways to do and this one is simply one that - works. - -The Subversion Repository -------------------------- - -For this article it's assumed that your repository layout follows the -widespread standard structure: - -.. code-block:: text - - myproject/ - branches/ - tags/ - trunk/ - -.. tip:: - - Most Subversion hosting should follow this standard practice. This - is the recommended layout in `Version Control with Subversion`_ and the - layout used by most free hosting (see :ref:`svn-hosting`). - -Initial Project Setup ---------------------- - -To get started, you'll need to download Symfony and get the basic Subversion setup. -First, download and get your Symfony project running by following the -:doc:`Installation ` article. - -Once you have your new project directory and things are working, follow along -with these steps: - -#. Checkout the Subversion repository that will host this project. Suppose - it is hosted on `Google code`_ and called ``myproject``: - - .. code-block:: terminal - - $ svn checkout http://myproject.googlecode.com/svn/trunk myproject - -#. Copy the Symfony project files in the Subversion folder: - - .. code-block:: terminal - - $ mv Symfony/* myproject/ - -#. Now, set the ignore rules. Not everything *should* be stored in your Subversion - repository. Some files (like the cache) are generated and others (like - the database configuration) are meant to be customized on each machine. - This makes use of the ``svn:ignore`` property, so that specific files can - be ignored. - - .. code-block:: terminal - - $ cd myproject/ - $ svn add --depth=empty app var var/cache var/logs app/config web - - $ svn propset svn:ignore "vendor" . - $ svn propset svn:ignore "bootstrap*" var/ - $ svn propset svn:ignore "parameters.yml" app/config/ - $ svn propset svn:ignore "*" var/cache/ - $ svn propset svn:ignore "*" var/logs/ - $ svn propset svn:ignore "*" var/sessions/ - - $ svn propset svn:ignore "bundles" web - - $ svn ci -m "commit basic Symfony ignore list (vendor, var/bootstrap*, app/config/parameters.yml, var/cache/*, var/logs/*, web/bundles)" - -#. The rest of the files can now be added and committed to the project: - - .. code-block:: terminal - - $ svn add --force . - $ svn ci -m "add basic Symfony Standard 3.X.Y" - -That's it! Since the ``app/config/parameters.yml`` file is ignored, you can -store machine-specific settings like database passwords here without committing -them. The ``parameters.yml.dist`` file *is* committed, but is not read by -Symfony. And by adding any new keys you need to both files, new developers -can quickly clone the project, copy this file to ``parameters.yml``, customize -it, and start developing. - -At this point, you have a fully-functional Symfony project stored in your -Subversion repository. The development can start with commits in the Subversion -repository. - -You can continue to follow along with the :doc:`/page_creation` article -to learn more about how to configure and develop inside your application. - -.. tip:: - - The Symfony Standard Edition comes with some example functionality. To - remove the sample code, follow the instructions in the - ":doc:`/bundles/remove`" article. - -.. include:: _vendor_deps.rst.inc - -.. _svn-hosting: - -Subversion Hosting Solutions ----------------------------- - -The biggest difference between `Git`_ and `SVN`_ is that Subversion *needs* a -central repository to work. You then have several solutions: - -- Self hosting: create your own repository and access it either through the - filesystem or the network. To help in this task you can read `Version Control - with Subversion`_. - -- Third party hosting: there are a lot of serious free hosting solutions - available like `GitHub`_, `Google code`_, `SourceForge`_ or `Gna`_. Some of them offer - Git hosting as well. - -.. _`Git`: https://git-scm.com/ -.. _`SVN`: https://subversion.apache.org/ -.. _`Subversion`: https://subversion.apache.org/ -.. _`Symfony Standard Edition`: https://symfony.com/download -.. _`Version Control with Subversion`: http://svnbook.red-bean.com/ -.. _`GitHub`: https://github.com/ -.. _`Google code`: https://code.google.com/hosting/ -.. _`SourceForge`: https://sourceforge.net/ -.. _`Gna`: http://gna.org/ diff --git a/setup/unstable_versions.rst b/setup/unstable_versions.rst index 9616ff7685e..0be7b3ad70d 100644 --- a/setup/unstable_versions.rst +++ b/setup/unstable_versions.rst @@ -7,54 +7,52 @@ they are released as stable versions. Creating a New Project Based on an Unstable Symfony Version ----------------------------------------------------------- -Suppose that Symfony 2.7 version hasn't been released yet and you want to create +Suppose that the Symfony 4.0 version hasn't been released yet and you want to create a new project to test its features. First, :doc:`install the Composer ` package manager. Then, open a command console, enter your project's directory and execute the following command: .. code-block:: terminal - $ composer create-project symfony/framework-standard-edition my_project "2.7.*" --stability=dev + # Download the latest beta version + $ composer create-project symfony/skeleton my_project "4.0.*" -s beta -Once the command finishes its execution, you'll have a new Symfony project created -in the ``my_project/`` directory and based on the most recent code found in the -``2.7`` branch. - -If you want to test a beta version, use ``beta`` as the value of the ``stability`` -option: - -.. code-block:: terminal + # Download the absolute latest commit + $ composer create-project symfony/skeleton my_project "4.0.*" -s dev - $ composer create-project symfony/framework-standard-edition my_project "2.7.*" --stability=beta +Once the command finishes its execution, you'll have a new Symfony project created +in the ``my_project/`` directory. Upgrading your Project to an Unstable Symfony Version ----------------------------------------------------- -Suppose again that Symfony 2.7 hasn't been released yet and you want to upgrade +Suppose again that Symfony 4.0 hasn't been released yet and you want to upgrade an existing application to test that your project works with it. First, open the ``composer.json`` file located in the root directory of your -project. Then, edit the value of the version defined for the ``symfony/symfony`` -dependency as follows: +project. Then, edit the value of all of the ``symfony/*`` libraries to the +new version and change your ``minimum-stability`` to ``beta``: -.. code-block:: json +.. code-block:: diff { "require": { - "symfony/symfony" : "2.7.*@dev" - } + + "symfony/framework-bundle": "^4.0", + + "symfony/finder": "^4.0", + "...": "..." + }, + + "minimum-stability": "beta" } -Finally, open a command console, enter your project directory and execute the -following command to update your project dependencies: +You can also use set ``minimum-stability`` to ``dev``, or omit this line +entirely, and opt into your stability on each package by using constraints +like ``4.0.*@beta``. -.. code-block:: terminal +Finally, from a terminal, update your project's dependencies: - $ composer update symfony/symfony +.. code-block:: terminal -If you prefer to test a Symfony beta version, replace the ``"2.7.*@dev"`` constraint -by ``"2.7.0-beta1"`` to install a specific beta number or ``2.7.*@beta`` to get -the most recent beta version. + $ composer update After upgrading the Symfony version, read the :ref:`Symfony Upgrading Guide ` to learn how you should proceed to update your application's code in case the new diff --git a/setup/upgrade_major.rst b/setup/upgrade_major.rst index 879fb6b900a..5e04ac2038f 100644 --- a/setup/upgrade_major.rst +++ b/setup/upgrade_major.rst @@ -1,7 +1,7 @@ .. index:: single: Upgrading; Major Version -Upgrading a Major Version (e.g. 2.7.0 to 3.0.0) +Upgrading a Major Version (e.g. 3.4.0 to 4.0.0) =============================================== Every two years, Symfony releases a new major version release (the first number @@ -30,10 +30,10 @@ backwards incompatible changes. To accomplish this, the "old" (e.g. functions, classes, etc) code still works, but is marked as *deprecated*, indicating that it will be removed/changed in the future and that you should stop using it. -When the major version is released (e.g. 3.0.0), all deprecated features and +When the major version is released (e.g. 4.0.0), all deprecated features and functionality are removed. So, as long as you've updated your code to stop using these deprecated features in the last version before the major (e.g. -2.8.*), you should be able to upgrade without a problem. +3.4.*), you should be able to upgrade without a problem. To help you with this, deprecation notices are triggered whenever you end up using a deprecated feature. When visiting your application in the @@ -75,7 +75,8 @@ Now, you can start fixing the notices: .. code-block:: text - $ phpunit + # this command is available after running "composer require --dev symfony/phpunit-bridge" + $ ./bin/phpunit ... OK (10 tests, 20 assertions) @@ -95,7 +96,7 @@ done! .. sidebar:: Using the Weak Deprecations Mode Sometimes, you can't fix all deprecations (e.g. something was deprecated - in 2.8 and you still need to support 2.7). In these cases, you can still + in 3.4 and you still need to support 3.3). In these cases, you can still use the bridge to fix as many deprecations as possible and then switch to the weak test mode to make your tests pass again. You can do this by using the ``SYMFONY_DEPRECATIONS_HELPER`` env variable: @@ -113,12 +114,6 @@ done! (you can also execute the command like ``SYMFONY_DEPRECATIONS_HELPER=weak phpunit``). -.. tip:: - - Some members of the Symfony Community have developed a tool called - `Symfony-Upgrade-Fixer`_ which automatically fixes some of the most common - deprecations found when upgrading from Symfony 2 to Symfony 3. - .. _upgrade-major-symfony-composer: 2) Update to the New Major Version via Composer @@ -133,7 +128,7 @@ Composer by modifying your ``composer.json`` file: "...": "...", "require": { - "symfony/symfony": "3.0.*", + "symfony/symfony": "^4.0", }, "...": "..." } @@ -153,10 +148,16 @@ Next, use Composer to download new versions of the libraries: 3) Update your Code to Work with the New Version ------------------------------------------------ -There is a good chance that you're done now! However, the next major version -*may* also contain new BC breaks as a BC layer is not always a possibility. -Make sure you read the ``UPGRADE-X.0.md`` (where X is the new major version) -included in the Symfony repository for any BC break that you need to be aware +The next major version *may* also contain new BC breaks as a BC layer is not always +a possibility. Make sure you read the ``UPGRADE-X.0.md`` (where X is the new major +version) included in the Symfony repository for any BC break that you need to be aware of. +4) Updating to the Symfony 4 Flex Directory Structure +----------------------------------------------------- + +When upgrading to Symfony 4, you will probably also want to upgrade to the new +Symfony 4 directory structure so that you can take advantage of Symfony Flex. +This takes some work, but is optional. For details, see :ref:`upgrade-to-flex`. + .. _`Symfony-Upgrade-Fixer`: https://github.com/umpirsky/Symfony-Upgrade-Fixer diff --git a/setup/upgrade_minor.rst b/setup/upgrade_minor.rst index 7c9f185a0a0..1cdf6e70109 100644 --- a/setup/upgrade_minor.rst +++ b/setup/upgrade_minor.rst @@ -1,7 +1,7 @@ .. index:: single: Upgrading; Minor Version -Upgrading a Minor Version (e.g. 2.5.3 to 2.6.1) +Upgrading a Minor Version (e.g. 3.3.3 to 3.4.0) =============================================== If you're upgrading a minor version (where the middle number changes), then @@ -30,7 +30,7 @@ to use the new version: "...": "...", "require": { - "symfony/symfony": "2.6.*", + "symfony/symfony": "3.4.*", }, "...": "...", } @@ -55,7 +55,7 @@ to your code to get everything working. Additionally, some features you're using might still work, but might now be deprecated. While that's just fine, if you know about these deprecations, you can start to fix them over time. -Every version of Symfony comes with an UPGRADE file (e.g. `UPGRADE-2.7.md`_) +Every version of Symfony comes with an UPGRADE file (e.g. `UPGRADE-3.4.md`_) included in the Symfony directory that describes these changes. If you follow the instructions in the document and update your code accordingly, it should be safe to update in the future. @@ -63,4 +63,4 @@ safe to update in the future. These documents can also be found in the `Symfony Repository`_. .. _`Symfony Repository`: https://github.com/symfony/symfony -.. _`UPGRADE-2.7.md`: https://github.com/symfony/symfony/blob/2.7/UPGRADE-2.7.md +.. _`UPGRADE-3.4.md`: https://github.com/symfony/symfony/blob/3.4/UPGRADE-3.4.md diff --git a/setup/upgrade_patch.rst b/setup/upgrade_patch.rst index 5e2c76e55cd..71140bb9411 100644 --- a/setup/upgrade_patch.rst +++ b/setup/upgrade_patch.rst @@ -1,7 +1,7 @@ .. index:: single: Upgrading; Patch Version -Upgrading a Patch Version (e.g. 2.6.0 to 2.6.1) +Upgrading a Patch Version (e.g. 3.3.2 to 3.3.3) =============================================== When a new patch version is released (only the last number changed), it is a @@ -15,7 +15,7 @@ version is *really* easy: That's it! You should not encounter any backwards-compatibility breaks or need to change anything else in your code. That's because when you started your project, your ``composer.json`` included Symfony using a constraint -like ``2.6.*``, where only the *last* version number will change when you +like ``3.3.*``, where only the *last* version number will change when you update. .. tip:: diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index 43d20495173..d3028532e76 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -15,22 +15,40 @@ When using Apache, you can configure PHP as an :ref:`PHP FPM `. FastCGI also is the preferred way to use PHP :ref:`with Nginx `. -.. sidebar:: The Web Directory +.. sidebar:: The public directory - The web directory is the home of all of your application's public and + The public directory is the home of all of your application's public and static files, including images, stylesheets and JavaScript files. It is - also where the front controllers (``app.php`` and ``app_dev.php``) live. + also where the front controller (``index.php``) lives. - The web directory serves as the document root when configuring your - web server. In the examples below, the ``web/`` directory will be the - document root. This directory is ``/var/www/project/web/``. + The public directory serves as the document root when configuring your + web server. In the examples below, the ``public/`` directory will be the + document root. This directory is ``/var/www/project/public/``. - If your hosting provider requires you to change the ``web/`` directory to + If your hosting provider requires you to change the ``public/`` directory to another location (e.g. ``public_html/``) make sure you - :ref:`override the location of the web/ directory `. + :ref:`override the location of the public/ directory `. .. _web-server-apache-mod-php: +Adding Rewrite Rules +-------------------- + +The easiest way is to install the Apache recipe by executing the following command: + +.. code-block:: terminal + + $ composer require symfony/apache-pack + +This recipe installs a ``.htaccess`` file in the ``public/`` directory that contains +the rewrite rules. + +.. tip:: + + A performance improvement can be achieved by moving the rewrite rules from the ``.htaccess`` + file into the VirtualHost block of your Apache configuration and then changing + ``AllowOverride All`` to ``AllowOverride None`` in your VirtualHost block. + Apache with mod_php/PHP-CGI --------------------------- @@ -42,8 +60,8 @@ The **minimum configuration** to get your application running under Apache is: ServerName domain.tld ServerAlias www.domain.tld - DocumentRoot /var/www/project/web - + DocumentRoot /var/www/project/public + AllowOverride All Order Allow,Deny Allow from All @@ -73,8 +91,8 @@ and increase web server performance: ServerName domain.tld ServerAlias www.domain.tld - DocumentRoot /var/www/project/web - + DocumentRoot /var/www/project/public + AllowOverride None Order Allow,Deny Allow from All @@ -83,7 +101,7 @@ and increase web server performance: Options -MultiViews RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ app.php [QSA,L] + RewriteRule ^(.*)$ index.php [QSA,L] @@ -96,13 +114,18 @@ and increase web server performance: # optionally disable the RewriteEngine for the asset directories # which will allow apache to simply reply with a 404 when files are # not found instead of passing the request into the full symfony stack - + RewriteEngine Off ErrorLog /var/log/apache2/project_error.log CustomLog /var/log/apache2/project_access.log combined + + # optionally set the value of the environment variables used in the application + #SetEnv APP_ENV prod + #SetEnv APP_SECRET + #SetEnv DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name" .. tip:: @@ -123,7 +146,7 @@ Hence, you need to modify your ``Directory`` permission settings as follows: .. code-block:: apache - + Require all granted # ... @@ -187,14 +210,14 @@ use the ``SetHandler`` directive to pass requests for PHP files to PHP FPM: # If you use Apache version below 2.4.9 you must consider update or use this instead - # ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1 + # ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/public/$1 # If you run your Symfony application on a subpath of your document root, the # regular expression must be changed accordingly: - # ProxyPassMatch ^/path-to-app/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1 + # ProxyPassMatch ^/path-to-app/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/public/$1 - DocumentRoot /var/www/project/web - + DocumentRoot /var/www/project/public + # enable the .htaccess rewrites AllowOverride All Require all granted @@ -228,8 +251,8 @@ should look something like this: Alias /php7-fcgi /usr/lib/cgi-bin/php7-fcgi FastCgiExternalServer /usr/lib/cgi-bin/php7-fcgi -host 127.0.0.1:9000 -pass-header Authorization - DocumentRoot /var/www/project/web - + DocumentRoot /var/www/project/public + # enable the .htaccess rewrites AllowOverride All Order Allow,Deny @@ -264,34 +287,23 @@ The **minimum configuration** to get your application running under Nginx is: server { server_name domain.tld www.domain.tld; - root /var/www/project/web; + root /var/www/project/public; location / { - # try to serve file directly, fallback to app.php - try_files $uri /app.php$is_args$args; + # try to serve file directly, fallback to index.php + try_files $uri /index.php$is_args$args; } - # DEV - # This rule should only be placed on your development environment - # In production, don't include this and don't deploy app_dev.php or config.php - location ~ ^/(app_dev|config)\.php(/|$) { - fastcgi_pass unix:/var/run/php/php7.1-fpm.sock; - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - # When you are using symlinks to link the document root to the - # current version of your application, you should pass the real - # application path instead of the path to the symlink to PHP - # FPM. - # Otherwise, PHP's OPcache may not properly detect changes to - # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126 - # for more information). - fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; - fastcgi_param DOCUMENT_ROOT $realpath_root; - } - # PROD - location ~ ^/app\.php(/|$) { + + location ~ ^/index\.php(/|$) { fastcgi_pass unix:/var/run/php/php7.1-fpm.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; + + # optionally set the value of the environment variables used in the application + # fastcgi_param APP_ENV prod; + # fastcgi_param APP_SECRET ; + # fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name"; + # When you are using symlinks to link the document root to the # current version of your application, you should pass the real # application path instead of the path to the symlink to PHP @@ -302,7 +314,7 @@ The **minimum configuration** to get your application running under Nginx is: fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; fastcgi_param DOCUMENT_ROOT $realpath_root; # Prevents URIs that include the front controller. This will 404: - # http://domain.tld/app.php/some-path + # http://domain.tld/index.php/some-path # Remove the internal directive to allow URIs like this internal; } @@ -324,17 +336,16 @@ The **minimum configuration** to get your application running under Nginx is: .. tip:: - This executes **only** ``app.php``, ``app_dev.php`` and ``config.php`` in - the web directory. All other files ending in ".php" will be denied. + This executes **only** ``index.php`` in the public directory. All other files + ending in ".php" will be denied. - If you have other PHP files in your web directory that need to be executed, + If you have other PHP files in your public directory that need to be executed, be sure to include them in the ``location`` block above. .. caution:: - After you deploy to production, make sure that you **cannot** access the ``app_dev.php`` - or ``config.php`` scripts (i.e. ``http://example.com/app_dev.php`` and ``http://example.com/config.php``). - If you *can* access these, be sure to remove the ``DEV`` section from the above configuration. + After you deploy to production, make sure that you **cannot** access the ``index.php`` + script (i.e. ``http://example.com/index.php``). .. note:: diff --git a/templating.rst b/templating.rst index bb129bf8839..7b9acc43515 100644 --- a/templating.rst +++ b/templating.rst @@ -174,7 +174,7 @@ First, build a base layout file: .. code-block:: html+twig - {# app/Resources/views/base.html.twig #} + {# templates/base.html.twig #} @@ -199,7 +199,7 @@ First, build a base layout file: .. code-block:: html+php - + @@ -242,7 +242,7 @@ A child template might look like this: .. code-block:: html+twig - {# app/Resources/views/blog/index.html.twig #} + {# templates/blog/index.html.twig #} {% extends 'base.html.twig' %} {% block title %}My cool blog posts{% endblock %} @@ -256,7 +256,7 @@ A child template might look like this: .. code-block:: html+php - + extend('base.html.php') ?> set('title', 'My cool blog posts') ?> @@ -270,7 +270,7 @@ A child template might look like this: .. note:: - The parent template is stored in ``app/Resources/views/``, so its path is + The parent template is stored in ``templates/``, so its path is simply ``base.html.twig``. The template naming conventions are explained fully in :ref:`template-naming-locations`. @@ -357,7 +357,7 @@ Template Naming and Locations By default, templates can live in two different locations: -``app/Resources/views/`` +``templates/`` The application's ``views`` directory can contain application-wide base templates (i.e. your application's layouts and templates of the application bundle) as well as templates that override third party bundle templates @@ -366,13 +366,13 @@ By default, templates can live in two different locations: ``vendor/path/to/CoolBundle/Resources/views/`` Each third party bundle houses its templates in its ``Resources/views/`` directory (and subdirectories). When you plan to share your bundle, you should - put the templates in the bundle instead of the ``app/`` directory. + put the templates in the bundle instead of the ``templates/`` directory. -Most of the templates you'll use live in the ``app/Resources/views/`` +Most of the templates you'll use live in the ``templates/`` directory. The path you'll use will be relative to this directory. For example, -to render/extend ``app/Resources/views/base.html.twig``, you'll use the +to render/extend ``templates/base.html.twig``, you'll use the ``base.html.twig`` path and to render/extend -``app/Resources/views/blog/index.html.twig``, you'll use the +``templates/blog/index.html.twig``, you'll use the ``blog/index.html.twig`` path. .. _template-referencing-in-bundle: @@ -407,7 +407,7 @@ for several types of templates, each which lives in a specific location: In the :doc:`/templating/overriding` section, you'll find out how each template living inside the AcmeBlogBundle, for example, can be overridden -by placing a template of the same name in the ``app/Resources/AcmeBlogBundle/views/`` +by placing a template of the same name in the ``templates/bundles/AcmeBlogBundle/`` directory. This gives the power to override templates from any vendor bundle. Template Suffix @@ -479,7 +479,7 @@ template. First, create the template that you'll need to reuse. .. code-block:: html+twig - {# app/Resources/views/article/article_details.html.twig #} + {# templates/article/article_details.html.twig #}

    {{ article.title }}

    @@ -489,7 +489,7 @@ template. First, create the template that you'll need to reuse. .. code-block:: html+php - +

    getTitle() ?>

    @@ -503,7 +503,7 @@ Including this template from any other template is simple: .. code-block:: html+twig - {# app/Resources/views/article/list.html.twig #} + {# templates/article/list.html.twig #} {% extends 'layout.html.twig' %} {% block body %} @@ -516,7 +516,7 @@ Including this template from any other template is simple: .. code-block:: html+php - + extend('layout.html.php') ?> start('body') ?> @@ -565,7 +565,7 @@ configuration: .. code-block:: php-annotations - // src/AppBundle/Controller/WelcomeController.php + // src/Controller/WelcomeController.php // ... use Symfony\Component\Routing\Annotation\Route; @@ -575,7 +575,7 @@ configuration: /** * @Route("/", name="welcome") */ - public function indexAction() + public function index() { // ... } @@ -583,14 +583,14 @@ configuration: .. code-block:: yaml - # app/config/routing.yml + # config/routes.yaml welcome: path: / - defaults: { _controller: AppBundle:Welcome:index } + controller: App\Controller\WelcomeController::index .. code-block:: xml - + - AppBundle:Welcome:index + App\Controller\WelcomeController::index .. code-block:: php - // app/config/routing.php + // config/routes.php use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; $routes = new RouteCollection(); $routes->add('welcome', new Route('/', array( - '_controller' => 'AppBundle:Welcome:index', + '_controller' => 'App\Controller\WelcomeController::index', ))); return $routes; @@ -634,7 +634,7 @@ route: .. code-block:: php-annotations - // src/AppBundle/Controller/ArticleController.php + // src/Controller/ArticleController.php // ... use Symfony\Component\Routing\Annotation\Route; @@ -644,7 +644,7 @@ route: /** * @Route("/article/{slug}", name="article_show") */ - public function showAction($slug) + public function show($slug) { // ... } @@ -652,14 +652,14 @@ route: .. code-block:: yaml - # app/config/routing.yml + # config/routes.yaml article_show: - path: /article/{slug} - defaults: { _controller: AppBundle:Article:show } + path: /article/{slug} + controller: App\Controller\ArticleController::show .. code-block:: xml - + - AppBundle:Article:show + App\Controller\ArticleController::show .. code-block:: php - // app/config/routing.php + // config/routes.php use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; $routes = new RouteCollection(); $routes->add('article_show', new Route('/article/{slug}', array( - '_controller' => 'AppBundle:Article:show', + '_controller' => 'App\Controller\ArticleController::show', ))); return $routes; @@ -693,7 +693,7 @@ correctly: .. code-block:: html+twig - {# app/Resources/views/article/recent_list.html.twig #} + {# templates/article/recent_list.html.twig #} {% for article in articles %} {{ article.title }} @@ -702,7 +702,7 @@ correctly: .. code-block:: html+php - + + loadFromExtension('framework', array( // ... 'templating' => array( @@ -64,31 +70,15 @@ You can now render a PHP template instead of a Twig one simply by using the ``.php`` extension in the template name instead of ``.twig``. The controller below renders the ``index.html.php`` template:: - // src/AppBundle/Controller/HelloController.php + // src/Controller/HelloController.php // ... - public function indexAction($name) + public function index($name) { - return $this->render( - 'AppBundle:Hello:index.html.php', - array('name' => $name) - ); - } - -You can also use the `@Template`_ shortcut to render the default -``AppBundle:Hello:index.html.php`` template:: - - // src/AppBundle/Controller/HelloController.php - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; - - // ... - - /** - * @Template(engine="php") - */ - public function indexAction($name) - { - return array('name' => $name); + // template is stored in src/Resources/views/hello/index.html.php + return $this->render('hello/index.html.php', array( + 'name' => $name + )); } .. caution:: @@ -98,24 +88,24 @@ You can also use the `@Template`_ shortcut to render the default the ``@`` notation for Twig namespaces will no longer be supported for the ``render()`` method:: - public function indexAction() + public function index() { // ... // namespaced templates will no longer work in controllers - $this->render('@App/Default/index.html.twig'); + $this->render('@SomeNamespace/hello/index.html.twig'); // you must use the traditional template notation - $this->render('AppBundle:Default:index.html.twig'); + $this->render('hello/index.html.twig'); } .. code-block:: twig {# inside a Twig template, namespaced templates work as expected #} - {{ include('@App/Default/index.html.twig') }} + {{ include('@SomeNamespace/hello/index.html.twig') }} {# traditional template notation will also work #} - {{ include('AppBundle:Default:index.html.twig') }} + {{ include('hello/index.html.twig') }} .. index:: single: Templating; Layout @@ -133,36 +123,29 @@ the ``extend()`` call: .. code-block:: html+php - - extend('AppBundle::layout.html.php') ?> + + extend('layout.html.php') ?> Hello ! -The ``AppBundle::layout.html.php`` notation sounds familiar, doesn't it? It -is the same notation used to reference a template. The ``::`` part simply -means that the controller element is empty, so the corresponding file is -directly stored under ``views/``. - Now, have a look at the ``layout.html.php`` file: .. code-block:: html+php - - extend('::base.html.php') ?> + + extend('base.html.php') ?>

    Hello Application

    output('_content') ?> -The layout is itself decorated by another one (``::base.html.php``). Symfony +The layout is itself decorated by another one (``base.html.php``). Symfony supports multiple decoration levels: a layout can itself be decorated by -another one. When the bundle part of the template name is empty, views are -looked for in the ``app/Resources/views/`` directory. This directory stores -global views for your entire project: +another one: .. code-block:: html+php - + @@ -195,8 +178,8 @@ decorating the template. In the ``index.html.php`` template, define a .. code-block:: html+php - - extend('AppBundle::layout.html.php') ?> + + extend('layout.html.php') ?> set('title', 'Hello World Application') ?> @@ -206,7 +189,7 @@ The base layout already has the code to output the title in the header: .. code-block:: html+php - + <?php $view['slots']->output('title', 'Hello Application') ?> @@ -237,17 +220,17 @@ Create a ``hello.html.php`` template: .. code-block:: html+php - + Hello ! And change the ``index.html.php`` template to include it: .. code-block:: html+php - - extend('AppBundle::layout.html.php') ?> + + extend('layout.html.php') ?> - render('AppBundle:Hello:hello.html.php', array('name' => $name)) ?> + render('hello/hello.html.php', array('name' => $name)) ?> The ``render()`` method evaluates and returns the content of another template (this is the exact same method as the one used in the controller). @@ -267,35 +250,17 @@ If you create a ``fancy`` action, and want to include it into the .. code-block:: html+php - + render( - new \Symfony\Component\HttpKernel\Controller\ControllerReference('AppBundle:Hello:fancy', array( - 'name' => $name, - 'color' => 'green', - )) + new \Symfony\Component\HttpKernel\Controller\ControllerReference( + 'App\Controller\HelloController::fancy', + array( + 'name' => $name, + 'color' => 'green', + ) + ) ) ?> -Here, the ``AppBundle:Hello:fancy`` string refers to the ``fancy`` action of the -``Hello`` controller:: - - // src/AppBundle/Controller/HelloController.php - - class HelloController extends Controller - { - public function fancyAction($name, $color) - { - // create some object, based on the $color variable - $object = ...; - - return $this->render('AppBundle:Hello:fancy.html.php', array( - 'name' => $name, - 'object' => $object, - )); - } - - // ... - } - But where is the ``$view['actions']`` array element defined? Like ``$view['slots']``, it's called a template helper, and the next section tells you more about those. @@ -331,10 +296,10 @@ pattern: .. code-block:: yaml - # src/AppBundle/Resources/config/routing.yml - hello: # The route name - path: /hello/{name} - defaults: { _controller: AppBundle:Hello:index } + # config/routes.yaml + hello: + path: /hello/{name} + controller: App\Controller\HelloController::index Using Assets: Images, JavaScripts and Stylesheets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/templating/debug.rst b/templating/debug.rst index a4b551451b0..91913843324 100644 --- a/templating/debug.rst +++ b/templating/debug.rst @@ -7,19 +7,25 @@ How to Dump Debug Information in Twig Templates =============================================== -When using PHP, you can use the +When using PHP templates, you can use the :ref:`dump() function from the VarDumper component ` -if you need to quickly find the value of a variable passed. This is useful, -for example, inside your controller:: +if you need to quickly find the value of a variable passed. First, make sure it +is installed: - // src/AppBundle/Controller/ArticleController.php - namespace AppBundle\Controller; +.. code-block:: terminal + + $ composer require var-dumper + +This is useful, for example, inside your controller:: + + // src/Controller/ArticleController.php + namespace App\Controller; // ... class ArticleController extends Controller { - public function recentListAction() + public function recentList() { $articles = ...; dump($articles); @@ -44,7 +50,7 @@ In a Twig template, you can use the ``dump`` utility as a function or a tag: .. code-block:: html+twig - {# app/Resources/views/article/recent_list.html.twig #} + {# templates/article/recent_list.html.twig #} {# the contents of this variable are sent to the Web Debug Toolbar #} {% dump articles %} diff --git a/templating/embedding_controllers.rst b/templating/embedding_controllers.rst index f8c640fe989..437ac6f080b 100644 --- a/templating/embedding_controllers.rst +++ b/templating/embedding_controllers.rst @@ -19,14 +19,14 @@ The solution is to simply embed the result of an entire controller from your template. First, create a controller that renders a certain number of recent articles:: - // src/AppBundle/Controller/ArticleController.php - namespace AppBundle\Controller; + // src/Controller/ArticleController.php + namespace App\Controller; // ... class ArticleController extends Controller { - public function recentArticlesAction($max = 3) + public function recentArticles($max = 3) { // make a database call or other logic // to get the "$max" most recent articles @@ -45,7 +45,7 @@ The ``recent_list`` template is perfectly straightforward: .. code-block:: html+twig - {# app/Resources/views/article/recent_list.html.twig #} + {# templates/article/recent_list.html.twig #} {% for article in articles %}
    {{ article.title }} @@ -54,7 +54,7 @@ The ``recent_list`` template is perfectly straightforward: .. code-block:: html+php - + getTitle() ?> @@ -68,31 +68,31 @@ The ``recent_list`` template is perfectly straightforward: you'll learn how to do this correctly. To include the controller, you'll need to refer to it using the standard -string syntax for controllers (i.e. **bundle**:**controller**:**action**): +string syntax for controllers (i.e. **controllerNamespace**::**action**): .. configuration-block:: .. code-block:: html+twig - {# app/Resources/views/base.html.twig #} + {# templates/base.html.twig #} {# ... #} .. code-block:: html+php - +