diff --git a/.alexrc b/.alexrc
new file mode 100644
index 00000000000..168d412c177
--- /dev/null
+++ b/.alexrc
@@ -0,0 +1,16 @@
+{
+ "allow": [
+ "attack",
+ "attacks",
+ "bigger",
+ "color",
+ "colors",
+ "failure",
+ "hook",
+ "hooks",
+ "host-hostess",
+ "invalid",
+ "remain",
+ "special"
+ ]
+}
diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml
new file mode 100644
index 00000000000..2ecd4fc7d2d
--- /dev/null
+++ b/.doctor-rst.yaml
@@ -0,0 +1,122 @@
+rules:
+ american_english: ~
+ avoid_repetetive_words: ~
+ blank_line_after_anchor: ~
+ blank_line_after_directive: ~
+ blank_line_before_directive: ~
+ composer_dev_option_not_at_the_end: ~
+ correct_code_block_directive_based_on_the_content: ~
+ deprecated_directive_should_have_version: ~
+ ensure_bash_prompt_before_composer_command: ~
+ ensure_correct_format_for_phpfunction: ~
+ ensure_exactly_one_space_before_directive_type: ~
+ ensure_exactly_one_space_between_link_definition_and_link: ~
+ ensure_explicit_nullable_types: ~
+ ensure_github_directive_start_with_prefix:
+ prefix: 'Symfony'
+ ensure_link_bottom: ~
+ ensure_link_definition_contains_valid_url: ~
+ ensure_order_of_code_blocks_in_configuration_block: ~
+ ensure_php_reference_syntax: ~
+ extend_abstract_controller: ~
+ # extension_xlf_instead_of_xliff: ~
+ forbidden_directives:
+ directives:
+ - '.. index::'
+ - directive: '.. caution::'
+ replacements: ['.. warning::', '.. danger::']
+ indention: ~
+ lowercase_as_in_use_statements: ~
+ max_blank_lines:
+ max: 2
+ max_colons: ~
+ no_app_console: ~
+ no_attribute_redundant_parenthesis: ~
+ no_blank_line_after_filepath_in_php_code_block: ~
+ no_blank_line_after_filepath_in_twig_code_block: ~
+ no_blank_line_after_filepath_in_xml_code_block: ~
+ no_blank_line_after_filepath_in_yaml_code_block: ~
+ no_brackets_in_method_directive: ~
+ no_broken_ref_directive: ~
+ no_composer_req: ~
+ no_directive_after_shorthand: ~
+ no_duplicate_use_statements: ~
+ no_empty_literals: ~
+ no_explicit_use_of_code_block_php: ~
+ no_footnotes: ~
+ no_inheritdoc: ~
+ no_merge_conflict: ~
+ no_namespace_after_use_statements: ~
+ no_php_open_tag_in_code_block_php_directive: ~
+ no_space_before_self_xml_closing_tag: ~
+ non_static_phpunit_assertions: ~
+ only_backslashes_in_namespace_in_php_code_block: ~
+ only_backslashes_in_use_statements_in_php_code_block: ~
+ ordered_use_statements: ~
+ php_prefix_before_bin_console: ~
+ remove_trailing_whitespace: ~
+ replace_code_block_types: ~
+ replacement: ~
+ short_array_syntax: ~
+ space_between_label_and_link_in_doc: ~
+ space_between_label_and_link_in_ref: ~
+ string_replacement: ~
+ title_underline_length_must_match_title_length: ~
+ typo: ~
+ unused_links: ~
+ use_deprecated_directive_instead_of_versionadded: ~
+ use_named_constructor_without_new_keyword_rule: ~
+ use_https_xsd_urls: ~
+ valid_inline_highlighted_namespaces: ~
+ valid_use_statements: ~
+ versionadded_directive_should_have_version: ~
+ yaml_instead_of_yml_suffix: ~
+
+ # master
+ versionadded_directive_major_version:
+ major_version: 7
+
+ versionadded_directive_min_version:
+ min_version: '7.0'
+
+ deprecated_directive_major_version:
+ major_version: 7
+
+ deprecated_directive_min_version:
+ min_version: '7.0'
+
+exclude_rule_for_file:
+ - path: configuration/multiple_kernels.rst
+ rule_name: replacement
+ - path: page_creation.rst
+ rule_name: no_php_open_tag_in_code_block_php_directive
+ - path: frontend/create_ux_bundle.rst
+ rule_name: argument_variable_must_match_type
+
+# do not report as violation
+whitelist:
+ regex:
+ - '/``.yml``/'
+ - '/(.*)\.orm\.yml/' # currently DoctrineBundle only supports .yml
+ lines:
+ - 'in config files, so the old ``app/config/config_dev.yml`` goes to'
+ - '#. The most important config file is ``app/config/services.yml``, which now is'
+ - 'The bin/console Command'
+ - '.. _`LDAP injection`: http://projects.webappsec.org/w/page/13246947/LDAP%20Injection'
+ - '.. versionadded:: 2.8.0' # Doctrine
+ - '.. versionadded:: 1.9.0' # Encore
+ - '.. versionadded:: 1.18' # Flex in setup/upgrade_minor.rst
+ - '.. versionadded:: 1.0.0' # Encore
+ - '.. versionadded:: 2.7.1' # Doctrine
+ - '123,' # assertion for var_dumper - components/var_dumper.rst
+ - '"foo",' # assertion for var_dumper - components/var_dumper.rst
+ - '$var .= "Because of this `\xE9` octet (\\xE9),\n";'
+ - '.. versionadded:: 0.2' # MercureBundle
+ - '.. versionadded:: 3.6' # MonologBundle
+ - '.. versionadded:: 3.8' # MonologBundle
+ - '.. versionadded:: 3.5' # Monolog
+ - '.. versionadded:: 3.0' # Doctrine ORM
+ - '.. _`a feature to test applications using Mercure`: https://github.com/symfony/panther#creating-isolated-browsers-to-test-apps-using-mercure-or-websocket'
+ - 'End to End Tests (E2E)'
+ - '.. versionadded:: 2.2.0' # Panther
+ - '* Inline code blocks use double-ticks (````like this````).'
diff --git a/.editorconfig b/.editorconfig
index 6f5286431d4..f9366facfb0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,8 +1,8 @@
root = true
-[*.{rst,rst.inc}]
+[*]
indent_style = space
-indent_size = 2
+indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000000..9eb5d91783b
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,30 @@
+# GithubActions workflows
+/.github/workflows* @OskarStark
+
+# Console
+/console* @chalasr
+/components/console* @chalasr
+
+# Form
+/forms.rst @xabbuh @HeahDude
+/components/form* @xabbuh @HeahDude
+/reference/forms* @xabbuh @HeahDude
+
+# PropertyInfo
+/components/property_info* @dunglas
+
+# Security
+/security* @chalasr
+/components/security* @chalasr
+
+# Validator
+/validation/* @xabbuh @HeahDude
+/components/validator* @xabbuh @HeahDude
+/reference/constraints* @xabbuh @HeahDude
+
+# Workflow
+/workflow* @lyrixx
+/components/workflow* @lyrixx
+
+# Yaml
+/components/yaml* @xabbuh
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000000..f32043e4523
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,9 @@
+
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 00000000000..497dfd9b430
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,145 @@
+name: CI
+
+on:
+ push:
+ branches-ignore:
+ - 'github-comments'
+ pull_request:
+ branches-ignore:
+ - 'github-comments'
+
+permissions:
+ contents: read
+
+jobs:
+ symfony-docs-builder-build:
+ name: Build (symfony-tools/docs-builder)
+
+ runs-on: ubuntu-latest
+
+ continue-on-error: true
+
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+
+ - name: "Set-up PHP"
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.4
+ coverage: none
+
+ - name: Get composer cache directory
+ id: composercache
+ working-directory: _build
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache dependencies
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composercache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: "Install dependencies"
+ working-directory: _build
+ run: composer install --prefer-dist --no-progress
+
+ - name: "Build the docs"
+ working-directory: _build
+ run: php build.php --disable-cache
+
+ doctor-rst:
+ name: Lint (DOCtor-RST)
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+
+ - name: "Create cache dir"
+ run: mkdir .cache
+
+ - name: "Extract base branch name"
+ run: echo "branch=$(echo ${GITHUB_BASE_REF:=${GITHUB_REF##*/}})" >> $GITHUB_OUTPUT
+ id: extract_base_branch
+
+ - name: "Cache DOCtor-RST"
+ uses: actions/cache@v3
+ with:
+ path: .cache
+ key: ${{ runner.os }}-doctor-rst-${{ steps.extract_base_branch.outputs.branch }}
+
+ - name: "Run DOCtor-RST"
+ uses: docker://oskarstark/doctor-rst:1.67.0
+ with:
+ args: --short --error-format=github --cache-file=/github/workspace/.cache/doctor-rst.cache
+
+ symfony-code-block-checker:
+ name: Code Blocks
+
+ runs-on: ubuntu-latest
+
+ continue-on-error: true
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ path: 'docs'
+
+ - name: Set-up PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.4
+ coverage: none
+
+ - name: Fetch branch from where the PR started
+ working-directory: docs
+ run: git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
+
+ - name: Find modified files
+ id: find-files
+ working-directory: docs
+ run: echo "files=$(git diff --name-only origin/${{ github.base_ref }} HEAD | grep ".rst" | tr '\n' ' ')" >> $GITHUB_OUTPUT
+
+ - name: Get composer cache directory
+ id: composercache
+ working-directory: docs/_build
+ run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
+
+ - name: Cache dependencies
+ if: ${{ steps.find-files.outputs.files }}
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.composercache.outputs.dir }}
+ key: ${{ runner.os }}-composer-codeBlocks-${{ hashFiles('_checker/composer.lock', '_sf_app/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-codeBlocks-
+
+ - name: Install dependencies
+ if: ${{ steps.find-files.outputs.files }}
+ run: composer create-project symfony-tools/code-block-checker:@dev _checker
+
+ - name: Install test application
+ if: ${{ steps.find-files.outputs.files }}
+ run: |
+ git clone -b ${{ github.base_ref }} --depth 5 --single-branch https://github.com/symfony-tools/symfony-application.git _sf_app
+ cd _sf_app
+ composer update
+
+ - name: Generate baseline
+ if: ${{ steps.find-files.outputs.files }}
+ working-directory: docs
+ run: |
+ CURRENT=$(git rev-parse HEAD)
+ git checkout -m ${{ github.base_ref }}
+ ../_checker/code-block-checker.php verify:docs `pwd` ${{ steps.find-files.outputs.files }} --generate-baseline=baseline.json --symfony-application=`realpath ../_sf_app`
+ git checkout -m $CURRENT
+ cat baseline.json
+
+ - name: Verify examples
+ if: ${{ steps.find-files.outputs.files }}
+ working-directory: docs
+ run: |
+ ../_checker/code-block-checker.php verify:docs `pwd` ${{ steps.find-files.outputs.files }} --baseline=baseline.json --output-format=github --symfony-application=`realpath ../_sf_app`
diff --git a/.gitignore b/.gitignore
index a5eb433eea3..b69047f69a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
-/_build/doctrees
-/_build/html
-*.pyc
+/_build/vendor
+/_build/output
diff --git a/.platform.app.yaml b/.platform.app.yaml
deleted file mode 100644
index 66f6a036b73..00000000000
--- a/.platform.app.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-# 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: symfonydocs
-
-# The toolstack used to build the application.
-type: "php"
-
-build:
- flavor: "composer"
-
-# The configuration of app when it is exposed to the web.
-web:
- # The public directory of the app, relative to its root.
- document_root: "/_build/html"
- index_files:
- - index.html
- whitelist:
- - \.html$
- - \.txt$
-
- # CSS and Javascript.
- - \.css$
- - \.js$
- - \.hbs$
-
- # image/* types.
- - \.gif$
- - \.png$
- - \.ico$
- - \.svgz?$
-
- # fonts types.
- - \.ttf$
- - \.eot$
- - \.woff$
- - \.otf$
-
- # robots.txt.
- - /robots\.txt$
-
-# The size of the persistent disk of the application (in MB).
-disk: 512
-
-# Build time dependencies.
-dependencies:
- python:
- virtualenv: 15.1.0
-
-# The hooks that will be performed when the package is deployed.
-hooks:
- build: |
- virtualenv .virtualenv
- . .virtualenv/bin/activate
- # Platform.sh currently sets PIP_USER=1.
- export PIP_USER=
- pip install pip==9.0.1 wheel==0.29.0
- pip install -r _build/.requirements.txt
- find .virtualenv -type f -name "*.rst" -delete
- make -C _build html
diff --git a/.platform/routes.yaml b/.platform/routes.yaml
deleted file mode 100644
index f99889ccec3..00000000000
--- a/.platform/routes.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-http://www.{default}/:
- to: http://{default}/
- type: redirect
-http://{default}/:
- cache:
- cookies:
- - '*'
- default_ttl: 0
- enabled: true
- headers:
- - Accept
- - Accept-Language
- ssi:
- enabled: false
- type: upstream
- upstream: symfonydocs:php
diff --git a/.platform/services.yaml b/.platform/services.yaml
deleted file mode 100644
index ec9369f2b00..00000000000
--- a/.platform/services.yaml
+++ /dev/null
@@ -1 +0,0 @@
-# Keeping this file empty to not deploy unused services.
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index cccb01eef30..00000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-language: python
-
-python: 2.7
-
-sudo: false
-cache:
- directories: [$HOME/.cache/pip]
-
-install: pip install -r _build/.requirements.txt
-
-script: make -C _build SPHINXOPTS=-nW html
-
-branches:
- except:
- - github-comments
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 00000000000..547ac103984
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,340 @@
+LICENSE
+=======
+
+**Creative Commons Attribution-ShareAlike 3.0 Unported**
+https://creativecommons.org/licenses/by-sa/3.0/
+
+-----
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS
+PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR
+OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS
+LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE
+BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED
+TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN
+CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
+
+1. Definitions
+--------------
+
+a. **"Adaptation"** means a work based upon the Work, or upon the Work and other
+pre-existing works, such as a translation, adaptation, derivative work,
+arrangement of music or other alterations of a literary or artistic work, or
+phonogram or performance and includes cinematographic adaptations or any other
+form in which the Work may be recast, transformed, or adapted including in any
+form recognizably derived from the original, except that a work that constitutes
+a Collection will not be considered an Adaptation for the purpose of this
+License. For the avoidance of doubt, where the Work is a musical work,
+performance or phonogram, the synchronization of the Work in timed-relation with
+a moving image ("synching") will be considered an Adaptation for the purpose of
+this License.
+
+b. **"Collection"** means a collection of literary or artistic works, such as
+encyclopedias and anthologies, or performances, phonograms or broadcasts, or
+other works or subject matter other than works listed in Section 1(f) below,
+which, by reason of the selection and arrangement of their contents, constitute
+intellectual creations, in which the Work is included in its entirety in
+unmodified form along with one or more other contributions, each constituting
+separate and independent works in themselves, which together are assembled into
+a collective whole. A work that constitutes a Collection will not be considered
+an Adaptation (as defined below) for the purposes of this License.
+
+c. **"Creative Commons Compatible License"** means a license that is listed at
+https://creativecommons.org/compatiblelicenses that has been approved by
+Creative Commons as being essentially equivalent to this License, including, at
+a minimum, because that license: (i) contains terms that have the same purpose,
+meaning and effect as the License Elements of this License; and, (ii) explicitly
+permits the relicensing of adaptations of works made available under that
+license under this License or a Creative Commons jurisdiction license with the
+same License Elements as this License.
+
+d. **"Distribute"** means to make available to the public the original and
+copies of the Work or Adaptation, as appropriate, through sale or other transfer
+of ownership.
+
+e. **"License Elements"** means the following high-level license attributes as
+selected by Licensor and indicated in the title of this License: Attribution,
+ShareAlike.
+
+f. **"Licensor"** means the individual, individuals, entity or entities that
+offer(s) the Work under the terms of this License.
+
+g. **"Original Author""** means, in the case of a literary or artistic work, the
+individual, individuals, entity or entities who created the Work or if no
+individual or entity can be identified, the publisher; and in addition (i) in
+the case of a performance the actors, singers, musicians, dancers, and other
+persons who act, sing, deliver, declaim, play in, interpret or otherwise perform
+literary or artistic works or expressions of folklore; (ii) in the case of a
+phonogram the producer being the person or legal entity who first fixes the
+sounds of a performance or other sounds; and, (iii) in the case of broadcasts,
+the organization that transmits the broadcast.
+
+h. **"Work"** means the literary and/or artistic work offered under the terms of
+this License including without limitation any production in the literary,
+scientific and artistic domain, whatever may be the mode or form of its
+expression including digital form, such as a book, pamphlet and other writing; a
+lecture, address, sermon or other work of the same nature; a dramatic or
+dramatico-musical work; a choreographic work or entertainment in dumb show; a
+musical composition with or without words; a cinematographic work to which are
+assimilated works expressed by a process analogous to cinematography; a work of
+drawing, painting, architecture, sculpture, engraving or lithography; a
+photographic work to which are assimilated works expressed by a process
+analogous to photography; a work of applied art; an illustration, map, plan,
+sketch or three-dimensional work relative to geography, topography, architecture
+or science; a performance; a broadcast; a phonogram; a compilation of data to
+the extent it is protected as a copyrightable work; or a work performed by a
+variety or circus performer to the extent it is not otherwise considered a
+literary or artistic work.
+
+i. **"You"** means an individual or entity exercising rights under this License
+who has not previously violated the terms of this License with respect to the
+Work, or who has received express permission from the Licensor to exercise
+rights under this License despite a previous violation.
+
+j. **"Publicly Perform"** means to perform public recitations of the Work and to
+communicate to the public those public recitations, by any means or process,
+including by wire or wireless means or public digital performances; to make
+available to the public Works in such a way that members of the public may
+access these Works from a place and at a place individually chosen by them; to
+perform the Work to the public by any means or process and the communication to
+the public of the performances of the Work, including by public digital
+performance; to broadcast and rebroadcast the Work by any means including signs,
+sounds or images.
+
+k. **"Reproduce"** means to make copies of the Work by any means including
+without limitation by sound or visual recordings and the right of fixation and
+reproducing fixations of the Work, including storage of a protected performance
+or phonogram in digital form or other electronic medium.
+
+2. Fair Dealing Rights
+----------------------
+
+Nothing in this License is intended to reduce, limit, or restrict any uses free
+from copyright or rights arising from limitations or exceptions that are
+provided for in connection with the copyright protection under copyright law or
+other applicable laws.
+
+3. License Grant
+----------------
+
+Subject to the terms and conditions of this License, Licensor hereby grants You
+a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the
+applicable copyright) license to exercise the rights in the Work as stated
+below:
+
+a. to Reproduce the Work, to incorporate the Work into one or more Collections,
+and to Reproduce the Work as incorporated in the Collections;
+
+b. to create and Reproduce Adaptations provided that any such Adaptation,
+including any translation in any medium, takes reasonable steps to clearly
+label, demarcate or otherwise identify that changes were made to the original
+Work. For example, a translation could be marked "The original work was
+translated from English to Spanish," or a modification could indicate "The
+original work has been modified.";
+
+c. to Distribute and Publicly Perform the Work including as incorporated in
+Collections; and,
+
+d. to Distribute and Publicly Perform Adaptations.
+
+e. For the avoidance of doubt:
+
+ 1. **Non-waivable Compulsory License Schemes.** In those jurisdictions in
+ which the right to collect royalties through any statutory or compulsory
+ licensing scheme cannot be waived, the Licensor reserves the exclusive
+ right to collect such royalties for any exercise by You of the rights
+ granted under this License;
+
+ 2. **Waivable Compulsory License Schemes.** In those jurisdictions in which
+ the right to collect royalties through any statutory or compulsory
+ licensing scheme can be waived, the Licensor waives the exclusive right to
+ collect such royalties for any exercise by You of the rights granted under
+ this License; and,
+
+ 3. **Voluntary License Schemes.** The Licensor waives the right to collect
+ royalties, whether individually or, in the event that the Licensor is a
+ member of a collecting society that administers voluntary licensing
+ schemes, via that society, from any exercise by You of the rights granted
+ under this License.
+
+The above rights may be exercised in all media and formats whether now known or
+hereafter devised. The above rights include the right to make such modifications
+as are technically necessary to exercise the rights in other media and formats.
+Subject to Section 8(f), all rights not expressly granted by Licensor are hereby
+reserved.
+
+4. Restrictions
+---------------
+
+The license granted in Section 3 above is expressly made subject to and limited
+by the following restrictions:
+
+a. You may Distribute or Publicly Perform the Work only under the terms of this
+License. You must include a copy of, or the Uniform Resource Identifier (URI)
+for, this License with every copy of the Work You Distribute or Publicly
+Perform. You may not offer or impose any terms on the Work that restrict the
+terms of this License or the ability of the recipient of the Work to exercise
+the rights granted to that recipient under the terms of the License. You may not
+sublicense the Work. You must keep intact all notices that refer to this License
+and to the disclaimer of warranties with every copy of the Work You Distribute
+or Publicly Perform. When You Distribute or Publicly Perform the Work, You may
+not impose any effective technological measures on the Work that restrict the
+ability of a recipient of the Work from You to exercise the rights granted to
+that recipient under the terms of the License. This Section 4(a) applies to the
+Work as incorporated in a Collection, but this does not require the Collection
+apart from the Work itself to be made subject to the terms of this License. If
+You create a Collection, upon notice from any Licensor You must, to the extent
+practicable, remove from the Collection any credit as required by Section 4(c),
+as requested. If You create an Adaptation, upon notice from any Licensor You
+must, to the extent practicable, remove from the Adaptation any credit as
+required by Section 4(c), as requested.
+
+b. You may Distribute or Publicly Perform an Adaptation only under the terms of:
+(i) this License; (ii) a later version of this License with the same License
+Elements as this License; (iii) a Creative Commons jurisdiction license (either
+this or a later license version) that contains the same License Elements as this
+License (e.g. Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons
+Compatible License. If you license the Adaptation under one of the licenses
+mentioned in (iv), you must comply with the terms of that license. If you
+license the Adaptation under the terms of any of the licenses mentioned in (i),
+(ii) or (iii) (the "Applicable License"), you must comply with the terms of the
+Applicable License generally and the following provisions: (I) You must include
+a copy of, or the URI for, the Applicable License with every copy of each
+Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose
+any terms on the Adaptation that restrict the terms of the Applicable License or
+the ability of the recipient of the Adaptation to exercise the rights granted to
+that recipient under the terms of the Applicable License; (III) You must keep
+intact all notices that refer to the Applicable License and to the disclaimer of
+warranties with every copy of the Work as included in the Adaptation You
+Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the
+Adaptation, You may not impose any effective technological measures on the
+Adaptation that restrict the ability of a recipient of the Adaptation from You
+to exercise the rights granted to that recipient under the terms of the
+Applicable License. This Section 4(b) applies to the Adaptation as incorporated
+in a Collection, but this does not require the Collection apart from the
+Adaptation itself to be made subject to the terms of the Applicable License.
+
+c. If You Distribute, or Publicly Perform the Work or any Adaptations or
+Collections, You must, unless a request has been made pursuant to Section 4(a),
+keep intact all copyright notices for the Work and provide, reasonable to the
+medium or means You are utilizing: (i) the name of the Original Author (or
+pseudonym, if applicable) if supplied, and/or if the Original Author and/or
+Licensor designate another party or parties (e.g. a sponsor institute,
+publishing entity, journal) for attribution ("Attribution Parties") in
+Licensor's copyright notice, terms of service or by other reasonable means, the
+name of such party or parties; (ii) the title of the Work if supplied; (iii) to
+the extent reasonably practicable, the URI, if any, that Licensor specifies to
+be associated with the Work, unless such URI does not refer to the copyright
+notice or licensing information for the Work; and (iv) , consistent with Section
+3(b), in the case of an Adaptation, a credit identifying the use of the Work in
+the Adaptation (e.g. "French translation of the Work by Original Author," or
+"Screenplay based on original Work by Original Author"). The credit required by
+this Section 4(c) may be implemented in any reasonable manner; provided,
+however, that in the case of a Adaptation or Collection, at a minimum such
+credit will appear, if a credit for all contributing authors of the Adaptation
+or Collection appears, then as part of these credits and in a manner at least as
+prominent as the credits for the other contributing authors. For the avoidance
+of doubt, You may only use the credit required by this Section for the purpose
+of attribution in the manner set out above and, by exercising Your rights under
+this License, You may not implicitly or explicitly assert or imply any
+connection with, sponsorship or endorsement by the Original Author, Licensor
+and/or Attribution Parties, as appropriate, of You or Your use of the Work,
+without the separate, express prior written permission of the Original Author,
+Licensor and/or Attribution Parties.
+
+d. Except as otherwise agreed in writing by the Licensor or as may be otherwise
+permitted by applicable law, if You Reproduce, Distribute or Publicly Perform
+the Work either by itself or as part of any Adaptations or Collections, You must
+not distort, mutilate, modify or take other derogatory action in relation to the
+Work which would be prejudicial to the Original Author's honor or reputation.
+Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise
+of the right granted in Section 3(b) of this License (the right to make
+Adaptations) would be deemed to be a distortion, mutilation, modification or
+other derogatory action prejudicial to the Original Author's honor and
+reputation, the Licensor will waive or not assert, as appropriate, this Section,
+to the fullest extent permitted by the applicable national law, to enable You to
+reasonably exercise Your right under Section 3(b) of this License (right to make
+Adaptations) but not otherwise.
+
+5. Representations, Warranties and Disclaimer
+---------------------------------------------
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS
+THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING
+THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
+LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR
+PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY,
+OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME
+JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH
+EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability
+--------------------------
+
+EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE
+LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,
+PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
+WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+--------------
+
+a. This License and the rights granted hereunder will terminate automatically
+upon any breach by You of the terms of this License. Individuals or entities who
+have received Adaptations or Collections from You under this License, however,
+will not have their licenses terminated provided such individuals or entities
+remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8
+will survive any termination of this License.
+
+b. Subject to the above terms and conditions, the license granted here is
+perpetual (for the duration of the applicable copyright in the Work).
+Notwithstanding the above, Licensor reserves the right to release the Work under
+different license terms or to stop distributing the Work at any time; provided,
+however that any such election will not serve to withdraw this License (or any
+other license that has been, or is required to be, granted under the terms of
+this License), and this License will continue in full force and effect unless
+terminated as stated above.
+
+8. Miscellaneous
+----------------
+
+a. Each time You Distribute or Publicly Perform the Work or a Collection, the
+Licensor offers to the recipient a license to the Work on the same terms and
+conditions as the license granted to You under this License.
+
+b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers
+to the recipient a license to the original Work on the same terms and conditions
+as the license granted to You under this License.
+
+c. If any provision of this License is invalid or unenforceable under applicable
+law, it shall not affect the validity or enforceability of the remainder of the
+terms of this License, and without further action by the parties to this
+agreement, such provision shall be reformed to the minimum extent necessary to
+make such provision valid and enforceable.
+
+d. No term or provision of this License shall be deemed waived and no breach
+consented to unless such waiver or consent shall be in writing and signed by the
+party to be charged with such waiver or consent.
+
+e. This License constitutes the entire agreement between the parties with
+respect to the Work licensed here. There are no understandings, agreements or
+representations with respect to the Work not specified here. Licensor shall not
+be bound by any additional provisions that may appear in any communication from
+You. This License may not be modified without the mutual written agreement of
+the Licensor and You.
+
+f. The rights granted under, and the subject matter referenced, in this License
+were drafted utilizing the terminology of the Berne Convention for the
+Protection of Literary and Artistic Works (as amended on September 28, 1979),
+the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO
+Performances and Phonograms Treaty of 1996 and the Universal Copyright
+Convention (as revised on July 24, 1971). These rights and subject matter take
+effect in the relevant jurisdiction in which the License terms are sought to be
+enforced according to the corresponding provisions of the implementation of
+those treaty provisions in the applicable national law. If the standard suite of
+rights granted under applicable copyright law includes additional rights not
+granted under this License, such additional rights are deemed to be included in
+the License; this License is not intended to restrict the license of any rights
+under applicable law.
diff --git a/README.markdown b/README.markdown
deleted file mode 100644
index 8d01c3cdcb7..00000000000
--- a/README.markdown
+++ /dev/null
@@ -1,21 +0,0 @@
-Symfony Documentation
-=====================
-
-This documentation is rendered online at https://symfony.com/doc/current/
-
-Contributing
-------------
-
->**Note**
->Unless you're documenting a feature that was introduced *after* Symfony 2.7
->(e.g. in Symfony 2.8), all pull requests must be based off of the **2.7** branch,
->**not** the master or older branches.
-
-We love contributors! For more information on how you can contribute to the
-Symfony documentation, please read
-[Contributing to the Documentation](https://symfony.com/doc/current/contributing/documentation/overview.html)
-
-Platform.sh
------------
-
-Pull requests are automatically built by [Platform.sh](https://platform.sh).
diff --git a/README.md b/README.md
new file mode 100644
index 00000000000..5c063058c02
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+
+
+Contributing
+------------
+
+We love contributors! For more information on how you can contribute, please read
+the [Symfony Docs Contributing Guide](https://symfony.com/doc/current/contributing/documentation/overview.html).
+
+> [!IMPORTANT]
+> Use `6.4` branch as the base of your pull requests, unless you are documenting a
+> feature that was introduced *after* Symfony 6.4 (e.g. in Symfony 7.2).
+
+Build Documentation Locally
+---------------------------
+
+This is not needed for contributing, but it's useful if you would like to debug some
+issue in the docs or if you want to read Symfony Documentation offline.
+
+```bash
+$ git clone git@github.com:symfony/symfony-docs.git
+
+$ cd symfony-docs/
+$ cd _build/
+
+$ composer install
+
+$ php build.php
+```
+
+After generating docs, serve them with the internal PHP server:
+
+```bash
+$ php -S localhost:8000 -t output/
+```
+
+Browse `http://localhost:8000` to read the docs.
diff --git a/_build/.requirements.txt b/_build/.requirements.txt
deleted file mode 100644
index 4a52e3fcb7e..00000000000
--- a/_build/.requirements.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-alabaster==0.7.10
-Babel==2.4.0
-docutils==0.13.1
-imagesize==0.7.1
-Jinja2==2.9.6
-MarkupSafe==1.0
-Pygments==2.2.0
-pytz==2017.2
-requests==2.12.5
-six==1.10.0
-snowballstemmer==1.2.1
-Sphinx==1.3.6
-git+https://github.com/fabpot/sphinx-php.git@7312eccce9465640752e51373a480da700e02345#egg_name=sphinx-php
diff --git a/_build/Makefile b/_build/Makefile
deleted file mode 100644
index 25b660056fe..00000000000
--- a/_build/Makefile
+++ /dev/null
@@ -1,153 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = .
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -c $(BUILDDIR) -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) ../
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-help:
- @echo "Please use \`make ' where is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Symfony.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Symfony.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/Symfony"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Symfony"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/_build/_theme/_exts/symfonycom/__init__.py b/_build/_theme/_exts/symfonycom/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/_build/_theme/_exts/symfonycom/sphinx/__init__.py b/_build/_theme/_exts/symfonycom/sphinx/__init__.py
deleted file mode 100644
index 1c08bcc11c8..00000000000
--- a/_build/_theme/_exts/symfonycom/sphinx/__init__.py
+++ /dev/null
@@ -1,167 +0,0 @@
-from sphinx.highlighting import lexers, PygmentsBridge
-from pygments.style import Style
-from pygments.formatters import HtmlFormatter
-from pygments.token import Keyword, Name, Comment, String, Error, \
- Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
-
-from sphinx.writers.html import HTMLTranslator
-from docutils import nodes
-from sphinx.locale import admonitionlabels, lazy_gettext
-
-customadmonitionlabels = admonitionlabels
-l_ = lazy_gettext
-customadmonitionlabels['best-practice'] = l_('Best Practice')
-
-def _getType(path):
- return path[:path.find('/')]
-
-def _isIndex(path):
- return 'index' in path
-
-class SensioHTMLTranslator(HTMLTranslator):
- def __init__(self, builder, *args, **kwds):
- HTMLTranslator.__init__(self, builder, *args, **kwds)
- builder.templates.environment.filters['get_type'] = _getType
- builder.templates.environment.tests['index'] = _isIndex
- self.highlightlinenothreshold = 0
-
- def visit_literal(self, node):
- self.body.append(self.starttag(node, 'tt', '', CLASS='docutils literal'))
- self.body.append('')
-
- def depart_literal(self, node):
- self.body.append('')
- self.body.append('')
-
- def visit_admonition(self, node, name=''):
- self.body.append(self.starttag(node, 'div', CLASS=('admonition-wrapper')))
- self.body.append('')
- self.body.append('
')
- if name and name != 'seealso':
- node.insert(0, nodes.title(name, customadmonitionlabels[name]))
- self.set_first_last(node)
-
- def depart_admonition(self, node=None):
- self.body.append('
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.
-
-
-
-
-{% endblock %}
-
-{# relbar1 is at the top and should not render the quick navigation #}
-{% block relbar1 %}{% endblock %}
-{% block relbar2 %}{% endblock %}
-
-{# remove "generated by sphinx" footer #}
-{% block footer %}{% endblock %}
diff --git a/_build/_theme/_templates/localtoc.html b/_build/_theme/_templates/localtoc.html
deleted file mode 100644
index 0ffea6e1ecd..00000000000
--- a/_build/_theme/_templates/localtoc.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
{{ _('Table Of Contents') }}
-
- {{ toc }}
-
-
diff --git a/_build/build.php b/_build/build.php
new file mode 100755
index 00000000000..b684700a848
--- /dev/null
+++ b/_build/build.php
@@ -0,0 +1,91 @@
+#!/usr/bin/env php
+register('build-docs')
+ ->addOption('generate-fjson-files', null, InputOption::VALUE_NONE, 'Use this option to generate docs both in HTML and JSON formats')
+ ->addOption('disable-cache', null, InputOption::VALUE_NONE, 'Use this option to force a full regeneration of all doc contents')
+ ->setCode(function(InputInterface $input, OutputInterface $output) {
+ // the doc building app doesn't work on Windows
+ if ('\\' === DIRECTORY_SEPARATOR) {
+ $output->writeln('ERROR: The application that builds Symfony Docs does not support Windows. You can try using a Linux distribution via WSL (Windows Subsystem for Linux).');
+
+ return 1;
+ }
+
+ $io = new SymfonyStyle($input, $output);
+ $io->text('Building all Symfony Docs...');
+
+ $outputDir = __DIR__.'/output';
+ $buildConfig = (new BuildConfig())
+ ->setSymfonyVersion('7.1')
+ ->setContentDir(__DIR__.'/..')
+ ->setOutputDir($outputDir)
+ ->setImagesDir(__DIR__.'/output/_images')
+ ->setImagesPublicPrefix('_images')
+ ->setTheme('rtd')
+ ;
+
+ $buildConfig->setExcludedPaths(['.github/', '_build/']);
+
+ if (!$generateJsonFiles = $input->getOption('generate-fjson-files')) {
+ $buildConfig->disableJsonFileGeneration();
+ }
+
+ if ($isCacheDisabled = $input->getOption('disable-cache')) {
+ $buildConfig->disableBuildCache();
+ }
+
+ $io->comment(sprintf('cache: %s / output file type(s): %s', $isCacheDisabled ? 'disabled' : 'enabled', $generateJsonFiles ? 'HTML and JSON' : 'HTML'));
+ if (!$isCacheDisabled) {
+ $io->comment('Tip: add the --disable-cache option to this command to force the re-build of all docs.');
+ }
+
+ $result = (new DocBuilder())->build($buildConfig);
+
+ if ($result->isSuccessful()) {
+ // fix assets URLs to make them absolute (otherwise, they don't work in subdirectories)
+ $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($outputDir));
+
+ foreach (new RegexIterator($iterator, '/^.+\.html$/i', RegexIterator::GET_MATCH) as $match) {
+ $htmlFilePath = array_shift($match);
+ $htmlContents = file_get_contents($htmlFilePath);
+
+ $htmlRelativeFilePath = str_replace($outputDir.'/', '', $htmlFilePath);
+ $subdirLevel = substr_count($htmlRelativeFilePath, '/');
+ $baseHref = str_repeat('../', $subdirLevel);
+
+ $htmlContents = str_replace('', '', $htmlContents);
+ $htmlContents = str_replace('success(sprintf("The Symfony Docs were successfully built at %s", realpath($outputDir)));
+ } else {
+ $io->error(sprintf("There were some errors while building the docs:\n\n%s\n", $result->getErrorTrace()));
+ $io->newLine();
+ $io->comment('Tip: you can add the -v, -vv or -vvv flags to this command to get debug information.');
+
+ return 1;
+ }
+
+ return 0;
+ })
+ ->getApplication()
+ ->setDefaultCommand('build-docs', true)
+ ->run();
diff --git a/_build/composer.json b/_build/composer.json
new file mode 100644
index 00000000000..f77976b10f4
--- /dev/null
+++ b/_build/composer.json
@@ -0,0 +1,22 @@
+{
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "config": {
+ "platform": {
+ "php": "8.3"
+ },
+ "preferred-install": {
+ "*": "dist"
+ },
+ "sort-packages": true,
+ "allow-plugins": {
+ "symfony/flex": true
+ }
+ },
+ "require": {
+ "php": ">=8.3",
+ "symfony/console": "^6.2",
+ "symfony/process": "^6.2",
+ "symfony-tools/docs-builder": "^0.27"
+ }
+}
diff --git a/_build/composer.lock b/_build/composer.lock
new file mode 100644
index 00000000000..b9a4646f8ae
--- /dev/null
+++ b/_build/composer.lock
@@ -0,0 +1,1792 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "e38eca557458275428db96db370d2c74",
+ "packages": [
+ {
+ "name": "doctrine/event-manager",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/event-manager.git",
+ "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e",
+ "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "conflict": {
+ "doctrine/common": "<2.9"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12",
+ "phpstan/phpstan": "^1.8.8",
+ "phpunit/phpunit": "^10.5",
+ "vimeo/psalm": "^5.24"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ },
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.",
+ "homepage": "https://www.doctrine-project.org/projects/event-manager.html",
+ "keywords": [
+ "event",
+ "event dispatcher",
+ "event manager",
+ "event system",
+ "events"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/event-manager/issues",
+ "source": "https://github.com/doctrine/event-manager/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-22T20:47:39+00:00"
+ },
+ {
+ "name": "doctrine/rst-parser",
+ "version": "0.5.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/rst-parser.git",
+ "reference": "ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104",
+ "reference": "ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/event-manager": "^1.0 || ^2.0",
+ "php": "^7.2 || ^8.0",
+ "symfony/filesystem": "^4.1 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/finder": "^4.1 || ^5.0 || ^6.0 || ^7.0",
+ "symfony/polyfill-mbstring": "^1.0",
+ "symfony/string": "^5.3 || ^6.0 || ^7.0",
+ "symfony/translation-contracts": "^1.1 || ^2.0 || ^3.0",
+ "twig/twig": "^2.9 || ^3.3"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11.0",
+ "gajus/dindent": "^2.0.2",
+ "phpstan/phpstan": "^1.9",
+ "phpstan/phpstan-deprecation-rules": "^1.0",
+ "phpstan/phpstan-phpunit": "^1.2",
+ "phpstan/phpstan-strict-rules": "^1.4",
+ "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0",
+ "symfony/css-selector": "4.4 || ^5.2 || ^6.0 || ^7.0",
+ "symfony/dom-crawler": "4.4 || ^5.2 || ^6.0 || ^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\RST\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Grégoire Passault",
+ "email": "g.passault@gmail.com",
+ "homepage": "http://www.gregwar.com/"
+ },
+ {
+ "name": "Jonathan H. Wage",
+ "email": "jonwage@gmail.com",
+ "homepage": "https://jwage.com"
+ }
+ ],
+ "description": "PHP library to parse reStructuredText documents and generate HTML or LaTeX documents.",
+ "homepage": "https://github.com/doctrine/rst-parser",
+ "keywords": [
+ "html",
+ "latex",
+ "markup",
+ "parser",
+ "reStructuredText",
+ "rst"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/rst-parser/issues",
+ "source": "https://github.com/doctrine/rst-parser/tree/0.5.6"
+ },
+ "time": "2024-01-14T11:02:23+00:00"
+ },
+ {
+ "name": "masterminds/html5",
+ "version": "2.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
+ },
+ "time": "2024-03-31T07:05:07+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "scrivo/highlight.php",
+ "version": "v9.18.1.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/scrivo/highlight.php.git",
+ "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/850f4b44697a2552e892ffe71490ba2733c2fc6e",
+ "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": ">=5.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8|^5.7",
+ "sabberworm/php-css-parser": "^8.3",
+ "symfony/finder": "^2.8|^3.4|^5.4",
+ "symfony/var-dumper": "^2.8|^3.4|^5.4"
+ },
+ "suggest": {
+ "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "HighlightUtilities/functions.php"
+ ],
+ "psr-0": {
+ "Highlight\\": "",
+ "HighlightUtilities\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Geert Bergman",
+ "homepage": "http://www.scrivo.org/",
+ "role": "Project Author"
+ },
+ {
+ "name": "Vladimir Jimenez",
+ "homepage": "https://allejo.io",
+ "role": "Maintainer"
+ },
+ {
+ "name": "Martin Folkers",
+ "homepage": "https://twobrain.io",
+ "role": "Contributor"
+ }
+ ],
+ "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js",
+ "keywords": [
+ "code",
+ "highlight",
+ "highlight.js",
+ "highlight.php",
+ "syntax"
+ ],
+ "support": {
+ "issues": "https://github.com/scrivo/highlight.php/issues",
+ "source": "https://github.com/scrivo/highlight.php"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/allejo",
+ "type": "github"
+ }
+ ],
+ "time": "2022-12-17T21:53:22+00:00"
+ },
+ {
+ "name": "symfony-tools/docs-builder",
+ "version": "0.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony-tools/docs-builder.git",
+ "reference": "720b52b2805122a4c08376496bd9661944c2624a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/720b52b2805122a4c08376496bd9661944c2624a",
+ "reference": "720b52b2805122a4c08376496bd9661944c2624a",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/rst-parser": "^0.5",
+ "ext-curl": "*",
+ "ext-json": "*",
+ "php": ">=8.3",
+ "scrivo/highlight.php": "^9.18.1",
+ "symfony/console": "^5.2 || ^6.0 || ^7.0",
+ "symfony/css-selector": "^5.2 || ^6.0 || ^7.0",
+ "symfony/dom-crawler": "^5.2 || ^6.0 || ^7.0",
+ "symfony/filesystem": "^5.2 || ^6.0 || ^7.0",
+ "symfony/finder": "^5.2 || ^6.0 || ^7.0",
+ "symfony/http-client": "^5.2 || ^6.0 || ^7.0",
+ "twig/twig": "^2.14 || ^3.3"
+ },
+ "require-dev": {
+ "gajus/dindent": "^2.0",
+ "masterminds/html5": "^2.7",
+ "symfony/phpunit-bridge": "^5.2 || ^6.0 || ^7.0",
+ "symfony/process": "^5.2 || ^6.0 || ^7.0"
+ },
+ "bin": [
+ "bin/docs-builder"
+ ],
+ "type": "project",
+ "autoload": {
+ "psr-4": {
+ "SymfonyDocsBuilder\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "The build system for Symfony's documentation",
+ "support": {
+ "issues": "https://github.com/symfony-tools/docs-builder/issues",
+ "source": "https://github.com/symfony-tools/docs-builder/tree/0.27.0"
+ },
+ "time": "2025-03-21T09:48:45+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v6.4.17",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "799445db3f15768ecc382ac5699e6da0520a0a04"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/799445db3f15768ecc382ac5699e6da0520a0a04",
+ "reference": "799445db3f15768ecc382ac5699e6da0520a0a04",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^5.4|^6.0|^7.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4",
+ "symfony/dotenv": "<5.4",
+ "symfony/event-dispatcher": "<5.4",
+ "symfony/lock": "<5.4",
+ "symfony/process": "<5.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0|^7.0",
+ "symfony/dependency-injection": "^5.4|^6.0|^7.0",
+ "symfony/event-dispatcher": "^5.4|^6.0|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^5.4|^6.0|^7.0",
+ "symfony/messenger": "^5.4|^6.0|^7.0",
+ "symfony/process": "^5.4|^6.0|^7.0",
+ "symfony/stopwatch": "^5.4|^6.0|^7.0",
+ "symfony/var-dumper": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v6.4.17"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-07T12:07:30+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2",
+ "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "symfony/dom-crawler",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dom-crawler.git",
+ "reference": "19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7",
+ "reference": "19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7",
+ "shasum": ""
+ },
+ "require": {
+ "masterminds/html5": "^2.6",
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "symfony/css-selector": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\DomCrawler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases DOM navigation for HTML and XML documents",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dom-crawler/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-17T15:53:07+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
+ "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-10-25T15:15:23+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v7.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "87a71856f2f56e4100373e92529eed3171695cfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb",
+ "reference": "87a71856f2f56e4100373e92529eed3171695cfb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v7.2.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-30T19:00:17+00:00"
+ },
+ {
+ "name": "symfony/http-client",
+ "version": "v7.2.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client.git",
+ "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6",
+ "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/http-client-contracts": "~3.4.4|^3.5.2",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "amphp/amp": "<2.5",
+ "php-http/discovery": "<1.15",
+ "symfony/http-foundation": "<6.4"
+ },
+ "provide": {
+ "php-http/async-client-implementation": "*",
+ "php-http/client-implementation": "*",
+ "psr/http-client-implementation": "1.0",
+ "symfony/http-client-implementation": "3.0"
+ },
+ "require-dev": {
+ "amphp/http-client": "^4.2.1|^5.0",
+ "amphp/http-tunnel": "^1.0|^2.0",
+ "amphp/socket": "^1.1",
+ "guzzlehttp/promises": "^1.4|^2.0",
+ "nyholm/psr7": "^1.0",
+ "php-http/httplug": "^1.0|^2.0",
+ "psr/http-client": "^1.0",
+ "symfony/amphp-http-client-meta": "^1.0|^2.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/rate-limiter": "^6.4|^7.0",
+ "symfony/stopwatch": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpClient\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "http"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/http-client/tree/v7.2.4"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-13T10:27:23+00:00"
+ },
+ {
+ "name": "symfony/http-client-contracts",
+ "version": "v3.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client-contracts.git",
+ "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645",
+ "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\HttpClient\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to HTTP clients",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-12-07T08:49:48+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
+ "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.31.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v6.4.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3",
+ "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Executes commands in sub-processes",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/process/tree/v6.4.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-04T13:35:48+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
+ "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82",
+ "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.1",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-13T13:31:26+00:00"
+ },
+ {
+ "name": "symfony/translation-contracts",
+ "version": "v3.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/translation-contracts.git",
+ "reference": "4667ff3bd513750603a09c8dedbea942487fb07c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c",
+ "reference": "4667ff3bd513750603a09c8dedbea942487fb07c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Translation\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to translation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-25T14:20:29+00:00"
+ },
+ {
+ "name": "twig/twig",
+ "version": "v3.20.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/twigphp/Twig.git",
+ "reference": "3468920399451a384bef53cf7996965f7cd40183"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183",
+ "reference": "3468920399451a384bef53cf7996965f7cd40183",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1.0",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-ctype": "^1.8",
+ "symfony/polyfill-mbstring": "^1.3"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^2.0",
+ "psr/container": "^1.0|^2.0",
+ "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/Resources/core.php",
+ "src/Resources/debug.php",
+ "src/Resources/escaper.php",
+ "src/Resources/string_loader.php"
+ ],
+ "psr-4": {
+ "Twig\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com",
+ "homepage": "http://fabien.potencier.org",
+ "role": "Lead Developer"
+ },
+ {
+ "name": "Twig Team",
+ "role": "Contributors"
+ },
+ {
+ "name": "Armin Ronacher",
+ "email": "armin.ronacher@active-4.com",
+ "role": "Project Founder"
+ }
+ ],
+ "description": "Twig, the flexible, fast, and secure template language for PHP",
+ "homepage": "https://twig.symfony.com",
+ "keywords": [
+ "templating"
+ ],
+ "support": {
+ "issues": "https://github.com/twigphp/Twig/issues",
+ "source": "https://github.com/twigphp/Twig/tree/v3.20.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/twig/twig",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-13T08:34:43+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {},
+ "prefer-stable": true,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=8.3"
+ },
+ "platform-dev": {},
+ "platform-overrides": {
+ "php": "8.3"
+ },
+ "plugin-api-version": "2.6.0"
+}
diff --git a/_build/conf.py b/_build/conf.py
deleted file mode 100644
index 8615b3cc714..00000000000
--- a/_build/conf.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Symfony documentation build configuration file, created by
-# sphinx-quickstart on Sat Jul 28 21:58:57 2012.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.append(os.path.abspath('_theme/_exts'))
-
-# adding PhpLexer
-from sphinx.highlighting import lexers
-from pygments.lexers.compiled import CLexer
-from pygments.lexers.special import TextLexer
-from pygments.lexers.text import RstLexer
-from pygments.lexers.web import PhpLexer
-from symfonycom.sphinx.lexer import TerminalLexer
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = [
- 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
- 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice', 'sensio.sphinx.codeblock',
- 'symfonycom.sphinx'
-]
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_theme/_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = 'Symfony Framework Documentation'
-copyright = ''
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-# version = '2.2'
-# The full version, including alpha/beta/rc tags.
-# release = '2.2.13'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# -- Settings for symfony doc extension ---------------------------------------------------
-
-# enable highlighting for PHP code not between ```` by default
-lexers['markdown'] = TextLexer()
-lexers['php'] = PhpLexer(startinline=True)
-lexers['php-annotations'] = PhpLexer(startinline=True)
-lexers['php-standalone'] = PhpLexer(startinline=True)
-lexers['php-symfony'] = PhpLexer(startinline=True)
-lexers['rst'] = RstLexer()
-lexers['varnish3'] = CLexer()
-lexers['varnish4'] = CLexer()
-lexers['terminal'] = TerminalLexer()
-
-config_block = {
- 'apache': 'Apache',
- 'markdown': 'Markdown',
- 'nginx': 'Nginx',
- 'rst': 'reStructuredText',
- 'terminal': 'Terminal',
- 'varnish3': 'Varnish 3',
- 'varnish4': 'Varnish 4'
-}
-
-# use PHP as the primary domain
-primary_domain = 'php'
-
-# set url for API links
-api_url = 'http://api.symfony.com/master/%s'
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'classic'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# " v documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'SymfonyDoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'Symfony.tex', u'Symfony Documentation',
- u'Symfony community', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'symfony', u'Symfony Documentation',
- [u'Symfony community'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'Symfony', u'Symfony Documentation',
- u'Symfony community', 'Symfony', 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# Use PHP syntax highlighting in code examples by default
-highlight_language='php'
diff --git a/_build/maintainer_guide.rst b/_build/maintainer_guide.rst
new file mode 100644
index 00000000000..9758b4e7397
--- /dev/null
+++ b/_build/maintainer_guide.rst
@@ -0,0 +1,378 @@
+Symfony Docs Maintainer Guide
+=============================
+
+The `symfony/symfony-docs`_ repository stores the Symfony project documentation
+and is managed by the `Symfony Docs team`_. This article explains in detail some
+of those management tasks, so it's only useful for maintainers and not regular
+readers or Symfony developers.
+
+Reviewing Pull Requests
+-----------------------
+
+All the recommendations of the `Symfony's respectful review comments`_ apply,
+but there are extra things to keep in mind for maintainers:
+
+* Always be nice in all interactions with all contributors.
+* Be extra-patient with new contributors (GitHub shows a special badge for them).
+* Don't assume that contributors know what you think is obvious (e.g. lots of
+ them don't know what to "squash commits" means).
+* Don't use acronyms like IMO, IIRC, etc. or complex English words (most
+ contributors are not native in English and it's intimidating for them).
+* Never engage in a heated discussion. Lock it right away using GitHub.
+* Never discuss non-tech issues. Some PRs are related to our Diversity initiative
+ and some people always try to drag you into politics. Never engage in that and
+ lock the issue/PR as off-topic on GitHub.
+
+Fixing Minor Issues Yourself
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It's common for new contributors to make lots of minor mistakes in the syntax
+of the RST format used in the docs. It's also common for non English speakers to
+make minor typos.
+
+Even if your intention is good, if you add lots of comments when reviewing a
+first contribution, that person will probably not contribute again. It's better
+to fix the minor errors and typos yourself while merging. If that person
+contributes again, it's OK to mention some of the minor issues to educate them.
+
+.. code-block:: terminal
+
+ $ gh merge 11059
+
+ Working on symfony/symfony-docs (branch 6.2)
+ Merging Pull Request 11059: dmaicher/patch-3
+
+ ...
+
+ # This is important!! Say NO to push the changes now
+ Push the changes now? (Y/n) n
+ Now, push with: git push gh "6.2" refs/notes/github-comments
+
+ # Now, open your editor and make the needed changes ...
+
+ $ git commit -a
+ # Use "Minor reword", "Minor tweak", etc. as the commit message
+
+ # now run the 'push' command shown above by 'gh' (it's different each time)
+ $ git push gh "6.2" refs/notes/github-comments
+
+Merging Pull Requests
+---------------------
+
+Technical Requirements
+~~~~~~~~~~~~~~~~~~~~~~
+
+* `Git`_ installed and properly configured.
+* ``gh`` tool fully installed according to its installation instructions
+ (GitHub token configured, Git remote configured, etc.)
+ This is a proprietary CLI tool which only Symfony team members have access to.
+* Some previous Git experience, specially merging pull requests.
+
+First Setup
+~~~~~~~~~~~
+
+First, fork the using the GitHub web
+interface. Then:
+
+.. code-block:: terminal
+
+ # Clone your fork
+ $ git clone https://github.com//symfony-docs.git
+
+ $ cd symfony-docs/
+
+ # Add the original repo as 'upstream' remote
+ $ git remote add upstream https://github.com/symfony/symfony-docs
+
+ # Add the original repo as 'gh' remote (needed for the 'gh' tool)
+ $ git remote add gh https://github.com/symfony/symfony-docs
+
+ # Configure 'gh' in Git as the remote used by the 'gh' tool
+ $ git config gh.remote gh
+
+Merging Process
+~~~~~~~~~~~~~~~
+
+At first, it's common to make mistakes and merge things badly. Don't worry. This
+has happened to all of us and we've always been able to recover from any mistake.
+
+Step 1: Select the right branch to merge
+........................................
+
+PRs must be merged in the oldest maintained branch where they are applicable:
+
+* Here you can find the currently maintained branches: https://symfony.com/roadmap.
+* Typos and old undocumented features are merged into the oldest maintained branch.
+* New features are merged into the branch where they were introduced. This
+ usually means ``master``. And don't forget to check that new feature includes
+ the ``versionadded`` directive.
+
+It's very common for contributors (specially newcomers) to select the wrong
+branch for their PRs, so we must always check if the change should go to the
+proposed branch or not.
+
+If the branch is wrong, there's no need to ask the contributor to rebase. The
+``gh`` tool can do that for us.
+
+Step 2: Merge the pull request
+..............................
+
+Never use GitHub's web interface (or desktop clients) to merge PRs or to solve
+merge conflicts. Always use the ``gh`` tool for anything related to merges.
+
+We require two approval votes from team members before merging a PR, except if
+it's a typo, a small change or clearly an error.
+
+If a PR contains lots of commits, there's no need to ask the contributor to
+squash them. The ``gh`` tool does that automatically. The only exceptions are
+when commits are made by more than one person and when there's a merge commit.
+``gh`` can't squash commits in those cases, so it's better to ask to the
+original contributor.
+
+.. code-block:: terminal
+
+ $ cd symfony-docs/
+
+ # make sure that your local branch is updated
+ $ git checkout 4.4
+ $ git fetch upstream
+ $ git merge upstream/4.4
+
+ # merge any PR passing its GitHub number as argument
+ $ gh merge 11159
+
+ # the gh tool will ask you some questions...
+
+ # push your changes (you can merge several PRs and push once at the end)
+ $ git push origin
+ $ git push upstream
+
+It's common to have to change the branch where a PR is merged. Instead of asking
+the contributors to rebase their PRs, the "gh" tool can change the branch with
+the ``-s`` option:
+
+.. code-block:: terminal
+
+ # e.g. this PR was sent against 'master', but it's merged in '4.4'
+ $ gh merge 11160 -s 4.4
+
+Sometimes, when changing the branch, you may face rebase issues, but they are
+usually simple to fix:
+
+.. code-block:: terminal
+
+ $ gh merge 11160 -s 4.4
+
+ ...
+
+ Unable to rebase the patch for pull/11183
+ The command "'git' 'rebase' '--onto' '4.4' '5.0' 'pull/11160'" failed.
+ Exit Code: 128(Invalid exit argument)
+
+ [...]
+ Auto-merging reference/forms/types/entity.rst
+ CONFLICT (content): Merge conflict in reference/forms/types/entity.rst
+ Patch failed at 0001 Update entity.rst
+ The copy of the patch that failed is found in: .git/rebase-apply/patch
+
+ # Now, fix all the conflicts using your editor
+
+ # Add the modified files and continue the rebase
+ $ git add reference/forms/types/entity.rst ...
+ $ git rebase --continue
+
+ # Lastly, re-run the exact same original command that resulted in a conflict
+ # There's no need to change the branch or do anything else.
+ $ gh merge 11160 -s 4.4
+
+ The previous run had some conflicts. Do you want to resume the merge? (Y/n)
+
+Later in this article you can find a troubleshooting section for the errors that
+you will usually face while merging.
+
+Step 3: Merge it into the other branches
+........................................
+
+If a PR has not been merged in ``master``, you must merge it up into all the
+maintained branches until ``master``. Imagine that you are merging a PR against
+``4.4`` and the maintained branches are ``4.4``, ``5.0`` and ``master``:
+
+.. code-block:: terminal
+
+ $ git fetch upstream
+
+ $ git checkout 4.4
+ $ git merge upstream/4.4
+
+ $ gh merge 11159
+ $ git push origin
+ $ git push upstream
+
+ $ git checkout 5.0
+ $ git merge upstream/5.0
+ $ git merge --log 4.4
+ # here you can face several errors explained later
+ $ git push origin
+ $ git push upstream
+
+ $ git checkout master
+ $ git merge upstream/master
+ $ git merge --log 5.0
+ $ git push origin
+ $ git push upstream
+
+.. tip::
+
+ If you followed the full ``gh`` installation instructions you can remove the
+ ``--log`` option in the above commands.
+
+.. tip::
+
+ When the support of a Symfony branch ends, it's recommended to delete your
+ local branch to avoid merging in it unawarely:
+
+ .. code-block:: terminal
+
+ # if Symfony 3.3 goes out of maintenance today, delete your local branch
+ $ git branch -D 3.3
+
+Troubleshooting
+~~~~~~~~~~~~~~~
+
+Wrong merge of your local branch
+................................
+
+When updating your local branches before merging:
+
+.. code-block:: terminal
+
+ $ git fetch upstream
+ $ git checkout 4.4
+ $ git merge upstream/4.4
+
+It's possible that you merge a wrong upstream branch unawarely. It's usually
+easy to spot because you'll see lots of conflicts:
+
+.. code-block:: terminal
+
+ # DON'T DO THIS! It's a wrong branch merge
+ $ git checkout 4.4
+ $ git merge upstream/5.0
+
+As long as you don't push this wrong merge, there's no problem. Delete your
+local branch and check it out again:
+
+.. code-block:: terminal
+
+ $ git checkout master
+ $ git branch -D 4.4
+ $ git checkout 4.4 upstream/4.4
+
+If you did push the wrong branch merge, ask for help in the documentation
+mergers chat and we'll help solve the problem.
+
+Solving merge conflicts
+.......................
+
+When merging things to upper branches, most of the times you'll see conflicts:
+
+.. code-block:: terminal
+
+ $ git checkout 5.0
+ $ git merge upstream/5.0
+ $ git merge --log 4.4
+
+ Auto-merging security/entity_provider.rst
+ Auto-merging logging/monolog_console.rst
+ Auto-merging form/dynamic_form_modification.rst
+ Auto-merging components/phpunit_bridge.rst
+ CONFLICT (content): Merge conflict in components/phpunit_bridge.rst
+ Automatic merge failed; fix conflicts and then commit the result.
+
+Solve the conflicts with your editor (look for occurrences of ``<<<<``, which is
+the marker used by Git for conflicts) and then do this:
+
+.. code-block:: terminal
+
+ # add all the conflicting files that you fixed
+ $ git add components/phpunit_bridge.rst
+ $ git commit -a
+ $ git push origin
+ $ git push upstream
+
+.. tip::
+
+ When there are lots of conflicts, look for ``<<<<<`` with your editor in all
+ docs before committing the changes. It's common to forget about some of them.
+ If you prefer, you can run this too: ``git grep --cached "<<<<<"``.
+
+Merging deleted files
+.....................
+
+A common cause of conflict when merging PRs into upper branches are files which
+were modified by the PR but no longer exist in newer branches:
+
+.. code-block:: terminal
+
+ $ git checkout 5.0
+ $ git merge upstream/5.0
+ $ git merge --log 4.4
+
+ Auto-merging translation/debug.rst
+ CONFLICT (modify/delete): service_container/scopes.rst deleted in HEAD and
+ modified in 4.4. Version 4.4 of service_container/scopes.rst left in tree.
+ Auto-merging service_container.rst
+
+If the contents of the deleted file were moved to a different file in newer
+branches, redo the changes in the new file. Then, delete the file that Git left
+in the tree as follows:
+
+.. code-block:: terminal
+
+ # delete all the conflicting files that no longer exist in this branch
+ $ git rm service_container/scopes.rst
+ $ git commit -a
+ $ git push origin
+ $ git push upstream
+
+Merging in the wrong branch
+...........................
+
+A Pull Request was made against ``5.x`` but it should be merged in ``5.1`` and you
+forgot to merge as ``gh merge NNNNN -s 5.1`` to change the merge branch. Solution:
+
+.. code-block:: terminal
+
+ $ git checkout 5.1
+ $ git cherry-pick -m 1
+ $ git checkout 5.x
+ $ git revert -m 1
+ # now continue with the normal "upmerging"
+ $ git checkout 5.2
+ $ git merge 5.1
+ $ ...
+
+Merging while the target branch changed
+.......................................
+
+Sometimes, someone else merges a PR in ``5.x`` at the same time as you are
+doing it. In these cases, ``gh merge ...`` fails to push. Solve this by
+resetting your local branch and restarting the merge:
+
+.. code-block:: terminal
+
+ $ gh merge ...
+ # this failed
+
+ # fetch the updated 5.x branch from GitHub
+ $ git fetch upstream
+ $ git checkout 5.x
+ $ git reset --hard upstream/5.x
+
+ # restart the merge
+ $ gh merge ...
+
+.. _`symfony/symfony-docs`: https://github.com/symfony/symfony-docs
+.. _`Symfony Docs team`: https://github.com/orgs/symfony/teams/team-symfony-docs
+.. _`Symfony's respectful review comments`: https://symfony.com/doc/current/contributing/community/review-comments.html
+.. _`Git`: https://git-scm.com/
diff --git a/_build/make.bat b/_build/make.bat
deleted file mode 100644
index 6d3f205272f..00000000000
--- a/_build/make.bat
+++ /dev/null
@@ -1,263 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=.
-set ALLSPHINXOPTS=-c %BUILDDIR% -d %BUILDDIR%/doctrees %SPHINXOPTS% ..
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^` where ^ is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- echo. coverage to run coverage check of the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-REM Check if sphinx-build is available and fallback to Python version if any
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 goto sphinx_python
-goto sphinx_ok
-
-:sphinx_python
-
-set SPHINXBUILD=python -m sphinx.__init__
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-:sphinx_ok
-
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Symfony.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Symfony.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %~dp0
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %~dp0
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "coverage" (
- %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of coverage in the sources finished, look at the ^
-results in %BUILDDIR%/coverage/python.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-:end
diff --git a/_build/redirection_map b/_build/redirection_map
index ad3565820f1..ee14c191025 100644
--- a/_build/redirection_map
+++ b/_build/redirection_map
@@ -13,7 +13,7 @@
/cookbook/email /email
/cookbook/gmail /cookbook/email/gmail
/cookbook/console /components/console
-/cookbook/tools/autoloader /components/class_loader
+/cookbook/tools/autoloader https://github.com/symfony/class-loader
/cookbook/tools/finder /components/finder
/cookbook/service_container/parentservices /service_container/parent_services
/cookbook/service_container/factories /service_container/factories
@@ -77,22 +77,31 @@
/book/configuration /configuration
/book/propel /propel/propel
/book/performance /performance
-/cookbook/assetic/apply_to_option /assetic/apply_to_option
-/cookbook/assetic/asset_management /assetic/asset_management
-/cookbook/assetic/index /assetic
-/cookbook/assetic/jpeg_optimize /assetic/jpeg_optimize
-/cookbook/assetic/php /assetic/php
-/cookbook/assetic/uglifyjs /assetic/uglifyjs
-/cookbook/assetic/yuicompressor /assetic/yuicompressor
+/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/index
+/cookbook/assetic/jpeg_optimize /frontend/assetic/jpeg_optimize
+/cookbook/assetic/php /frontend/assetic/php
+/cookbook/assetic/uglifyjs /frontend/assetic/uglifyjs
+/cookbook/assetic/yuicompressor /frontend/assetic/yuicompressor
+/assetic /frontend/assetic/index
+/assetic/apply_to_option /frontend/assetic/apply_to_option
+/assetic/asset_management /frontend/assetic/asset_management
+/assetic/jpeg_optimize /frontend/assetic/jpeg_optimize
+/assetic/php /frontend/assetic/php
+/assetic/uglifyjs /frontend/assetic/uglifyjs
+/assetic/yuicompressor /frontend/assetic/yuicompressor
/cookbook/bundles/best_practices /bundles/best_practices
/cookbook/bundles/configuration /bundles/configuration
/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
@@ -110,49 +119,52 @@
/cookbook/console/commands_as_services /console/commands_as_services
/cookbook/console/console_command /console
/cookbook/console/index /console
-/cookbook/console/logging /console/logging
+/cookbook/console/logging /console
/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/deployment/azure-website /cookbook/azure-website
-/cookbook/deployment/fortrabbit /deployment/fortrabbit
-/cookbook/deployment/heroku /deployment/heroku
-/cookbook/deployment/index /deployment
-/cookbook/deployment/platformsh /deployment/platformsh
+/cookbook/debugging /
+/debug/debugging /
/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
-/cookbook/event_dispatcher/before_after_filters /event_dispatcher/before_after_filters
+/cookbook/event_dispatcher/before_after_filters /event_dispatcher#event-dispatcher-before-after-filters
+/event_dispatcher/before_after_filters /event_dispatcher#event-dispatcher-before-after-filters
/cookbook/event_dispatcher/class_extension /event_dispatcher/class_extension
/cookbook/event_dispatcher/event_listener /event_dispatcher
/cookbook/event_dispatcher/index /event_dispatcher
/cookbook/event_dispatcher/method_behavior /event_dispatcher/method_behavior
-/cookbook/expressions /expressions/expressions
+/event_dispatcher/method_behavior /event_dispatcher#event-dispatcher-method-behavior
+/cookbook/expressions /security/expressions
+/expressions /security/expressions
/cookbook/form/create_custom_field_type /form/create_custom_field_type
/cookbook/form/create_form_type_extension /form/create_form_type_extension
/cookbook/form/data_transformers /form/data_transformers
@@ -164,7 +176,7 @@
/cookbook/form/inherit_data_option /form/inherit_data_option
/cookbook/form/unit_testing /form/unit_testing
/cookbook/form/use_empty_data /form/use_empty_data
-/cookbook/frontend/bower /frontend/bower
+/cookbook/frontend/bower /frontend
/cookbook/frontend/index /frontend
/cookbook/install/unstable_versions /setup/unstable_versions
/cookbook/install/bundles /setup/bundles
@@ -178,15 +190,16 @@
/cookbook/logging/monolog_console /logging/monolog_console
/cookbook/logging/monolog_email /logging/monolog_email
/cookbook/logging/monolog_regex_based_excludes /logging/monolog_regex_based_excludes
-/cookbook/profiler/data_collector /profiler/data_collector
+/cookbook/profiler/data_collector /profiler#profiler-data-collector
+/profiler/data_collector /profiler#profiler-data-collector
/cookbook/profiler/index /profiler
/cookbook/profiler/matchers /profiler/matchers
/cookbook/profiler/profiling_data /profiler/profiling_data
/cookbook/profiler/storage /profiler/storage
-/cookbook/psr7 /request/psr7
+/cookbook/psr7 /components/psr7
/cookbook/request/index /request
-/cookbook/request/load_balancer_reverse_proxy /request/load_balancer_reverse_proxy
-/cookbook/request/mime_type /request/mime_type
+/cookbook/request/load_balancer_reverse_proxy /deployment/proxies
+/cookbook/request/mime_type /reference/configuration/framework
/cookbook/routing/conditions /routing/conditions
/cookbook/routing/custom_route_loader /routing/custom_route_loader
/cookbook/routing/debug /routing/debug
@@ -206,7 +219,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
@@ -236,13 +249,16 @@
/cookbook/service_container/shared /service_container/shared
/cookbook/session/avoid_session_start /session/avoid_session_start
/cookbook/session/index /session
-/cookbook/session/limit_metadata_writes /session/limit_metadata_writes
-/cookbook/session/locale_sticky_session /session/locale_sticky_session
+/cookbook/session/limit_metadata_writes /reference/configuration/framework
+/session/limit_metadata_writes /reference/configuration/framework
+/cookbook/session/locale_sticky_session /session#locale-sticky-session
+/cookbook/locale_sticky_session /session#locale-sticky-session
/cookbook/session/php_bridge /session/php_bridge
/cookbook/session/proxy_examples /session/proxy_examples
/cookbook/session/sessions_directory /session/sessions_directory
/cookbook/symfony1 /introduction/symfony1
-/cookbook/templating/global_variables /templating/global_variables
+/cookbook/templating/global_variables /templating#templating-global-variables
+/templating/global_variables /templating#templating-global-variables
/cookbook/templating/index /templating
/cookbook/templating/namespaced_paths /templating/namespaced_paths
/cookbook/templating/PHP /templating/PHP
@@ -262,7 +278,7 @@
/cookbook/upgrade/minor_version /setup/upgrade_major
/cookbook/upgrade/patch_version /upgrade/bundles
/cookbook/validation/custom_constraint /validation/custom_constraint
-/cookbook/validation/group_service_resolver /validation/group_service_resolver
+/cookbook/validation/group_service_resolver /form/validation_group_service_resolver
/cookbook/validation/index /validation
/cookbook/validation/severity /validation/severity
/cookbook/web_server/built_in /setup/built_in_web_server
@@ -271,22 +287,35 @@
/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/introduction https://github.com/symfony/class-loader
+/components/class_loader/index https://github.com/symfony/class-loader
+/components/class_loader/cache_class_loader https://github.com/symfony/class-loader
+/components/class_loader/class_loader https://github.com/symfony/class-loader
+/components/class_loader/class_map_generator https://github.com/symfony/class-loader
+/components/class_loader/debug_class_loader https://github.com/symfony/class-loader
+/components/class_loader/map_class_loader https://github.com/symfony/class-loader
+/components/class_loader/map_class_loader https://github.com/symfony/class-loader
+/components/class_loader/psr4_class_loader https://github.com/symfony/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
/components/debug/introduction /components/debug
/components/debug/index /components/debug
/components/dependency_injection/advanced /service_container/alias_private
+/components/dependency_injection/autowiring /service_container/autowiring
/components/dependency_injection/definitions /service_container/definitions
/components/dependency_injection/introduction /components/dependency_injection
/components/dependency_injection/index /components/dependency_injection
@@ -310,32 +339,238 @@
/components/form/type_guesser /form/type_guesser
/components/http_foundation/index /components/http_foundation
/components/http_foundation/introduction /components/http_foundation
+/request/load_balancer_reverse_proxy /deployment/proxies
+/components/http_foundation/trusting_proxies /deployment/proxies
/components/http_kernel/introduction /components/http_kernel
/components/http_kernel/index /components/http_kernel
/components/property_access/introduction /components/property_access
/components/property_access/index /components/property_access
-/components/routing/index /components/routing
-/components/routing/introduction /components/routing
+/components/routing/index https://github.com/symfony/routing
+/components/routing/introduction https://github.com/symfony/routing
/components/routing/hostname_pattern /routing/hostname_pattern
/components/security/introduction /components/security
/components/security/index /components/security
-/components/templating/introduction /components/templating
-/components/templating/index /components/templating
-/components/templating/helpers/assetshelper /components/templating/assetshelper
-/components/templating/helpers/slotshelper /components/templating/slotshelper
+/components/templating/introduction https://github.com/symfony/templating
+/components/templating/index https://github.com/symfony/templating
+/components/templating/helpers/assetshelper https://github.com/symfony/templating
+/components/templating/helpers/slotshelper https://github.com/symfony/templating
/components/translation/introduction /components/translation
/components/translation/index /components/translation
/components/var_dumper/introduction /components/var_dumper
/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
-/form /forms
-/testing/simulating_authentication /testing/http_authentication
-/components/dependency_injection/autowiring /service_container/autowiring
+/email/gmail /email
+/email/cloud /email
/event_dispatcher/class_extension /event_dispatcher
+/form /forms
+/form/use_virtual_forms /form/inherit_data_option
+/frontend/assetic /frontend/assetic/index
+/frontend/assetic/apply_to_option /frontend/assetic/index
+/frontend/assetic/asset_management /frontend/assetic/index
+/frontend/assetic/jpeg_optimize /frontend/assetic/index
+/frontend/assetic/php /frontend/assetic/index
+/frontend/assetic/uglifyjs /frontend/assetic/index
+/frontend/assetic/yuicompressor /frontend/assetic/index
+/reference/configuration/assetic /frontend/assetic/index
/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
-/components/http_foundation/trusting_proxies /request/load_balancer_reverse_proxy
+/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
+/templating/overriding /bundles/override
+/templating/twig_extension /templates#templates-twig-extension
+/templating/hinclude /templates#templates-hinclude
+/templating/PHP /templates
+/security/custom_provider /security/user_provider
+/security/multiple_user_providers /security/user_provider
+/security/custom_password_authenticator /security/guard_authentication
+/security/api_key_authentication /security/guard_authentication
+/security/pre_authenticated /security/auth_providers
+/security/host_restriction /security/firewall_restriction
+/security/acl_advanced /security/acl
+/security/password_encoding /security
+/weblink /web_link
+/components/weblink https://github.com/symfony/web-link
+/frontend/encore/installation-no-flex /frontend/encore/installation
+/http_cache/form_csrf_caching /security/csrf
+/console/logging /console
+/reference/forms/twig_reference /form/form_customization
+/form/rendering /form/form_customization
+/profiler/matchers /profiler
+/profiler/profiling_data /profiler
+/profiler/wdt_follow_ajax /profiler
+/security/entity_provider /security/user_provider
+/session/avoid_session_start /session
+/session/sessions_directory /session
+/session/configuring_ttl /session#session-configure-ttl
+/frontend/encore/legacy-apps /frontend/encore/legacy-applications
+/configuration/external_parameters /configuration/environment_variables
+/contributing/code/patches /contributing/code/pull_requests
+/workflow/state-machines /workflow/workflow-and-state-machine
+/workflow/introduction /workflow/workflow-and-state-machine
+/workflow/usage /workflow
+/introduction/from_flat_php_to_symfony2 /introduction/from_flat_php_to_symfony
+/configuration/environment_variables /configuration/env_var_processors
+/configuration/configuration_organization /configuration
+/configuration/environments /configuration
+/configuration/configuration_organization /configuration
+/email/dev_environment /mailer
+/email/spool /mailer
+/email/testing /mailer
+/contributing/community/other /contributing/community
+/contributing/code/core_team /contributing/core_team
+/profiler/storage /profiler
+/setup/composer /setup
+/security/security_checker /setup
+/setup/built_in_web_server /setup/symfony_server
+/service_container/parameters /configuration
+/routing/generate_url_javascript /routing
+/routing/slash_in_parameter /routing
+/routing/scheme /routing
+/routing/optional_placeholders /routing
+/routing/conditions /routing
+/routing/requirements /routing
+/routing/redirect_trailing_slash /routing
+/routing/debug /routing
+/routing/service_container_parameters /routing
+/routing/redirect_in_config /routing
+/routing/external_resources /routing
+/routing/hostname_pattern /routing
+/routing/extra_information /routing
+/console/request_context /routing
+/form/action_method /forms
+/reference/requirements /setup
+/bundles/inheritance /bundles/override
+/templating /templates
+/templating/escaping /templates#output-escaping
+/templating/syntax /templates#linting-twig-templates
+/templating/debug /templates#the-dump-twig-utilities
+/templating/render_without_controller /templates#rendering-a-template-directly-from-a-route
+/templating/app_variable /templates#the-app-global-variable
+/templating/formats /templates
+/templating/namespaced_paths /templates#template-namespaces
+/templating/embedding_controllers /templates#embedding-controllers
+/templating/inheritance /templates#template-inheritance-and-layouts
+/testing/doctrine /testing/database
+/translation/templates /translation#translation-in-templates
+/translation/debug /translation#translation-debug
+/translation/lint /translation#translation-lint
+/translation/locale /translation#translation-locale
+/doctrine/lifecycle_callbacks /doctrine/events
+/doctrine/event_listeners_subscribers /doctrine/events
+/doctrine/common_extensions /doctrine
+/best_practices/index /best_practices
+/best_practices/introduction /best_practices
+/best_practices/creating-the-project /best_practices
+/best_practices/configuration /best_practices
+/best_practices/business-logic /best_practices
+/best_practices/controllers /best_practices
+/best_practices/templates /best_practices
+/best_practices/forms /best_practices
+/best_practices/i18n /best_practices
+/best_practices/security /best_practices
+/best_practices/web-assets /best_practices
+/best_practices/tests /best_practices
+/components/debug https://github.com/symfony/debug
+/components/translation https://github.com/symfony/translation
+/components/translation/usage /translation
+/components/translation/custom_formats https://github.com/symfony/translation
+/components/translation/custom_message_formatter https://github.com/symfony/translation
+/components/notifier https://github.com/symfony/notifier
+/components/routing https://github.com/symfony/routing
+/session/database /session#session-database
+/doctrine/pdo_session_storage /session#session-database-pdo
+/doctrine/mongodb_session_storage /session#session-database-mongodb
+/components/dotenv https://github.com/symfony/dotenv
+/components/mercure /mercure
+/components/polyfill_apcu https://github.com/symfony/polyfill-apcu
+/components/polyfill_ctype https://github.com/symfony/polyfill-ctype
+/components/polyfill_iconv https://github.com/symfony/polyfill-iconv
+/components/polyfill_intl_grapheme https://github.com/symfony/polyfill_intl-grapheme
+/components/polyfill_intl_icu https://github.com/symfony/polyfill_intl-icu
+/components/polyfill_intl_idn https://github.com/symfony/polyfill_intl-idn
+/components/polyfill_intl_normalizer https://github.com/symfony/polyfill_intl-normalizer
+/components/polyfill_mbstring https://github.com/symfony/polyfill-mbstring
+/components/polyfill_php54 https://github.com/symfony/polyfill-php54
+/components/polyfill_php55 https://github.com/symfony/polyfill-php55
+/components/polyfill_php56 https://github.com/symfony/polyfill-php56
+/components/polyfill_php70 https://github.com/symfony/polyfill-php70
+/components/polyfill_php71 https://github.com/symfony/polyfill-php71
+/components/polyfill_php72 https://github.com/symfony/polyfill-php72
+/components/polyfill_php73 https://github.com/symfony/polyfill-php73
+/components/polyfill_uuid https://github.com/symfony/polyfill-uuid
+/components/web_link https://github.com/symfony/web-link
+/components/templating https://github.com/symfony/templating
+/components/error_handler https://github.com/symfony/error-handler
+/components/class_loader https://github.com/symfony/class-loader
+/frontend/encore/versus-assetic /frontend
+/components/http_client /http_client
+/components/mailer /mailer
+/messenger/message-recorder /messenger/dispatch_after_current_bus
+/components/stopwatch https://github.com/symfony/stopwatch
+/service_container/3.3-di-changes https://symfony.com/doc/3.4/service_container/3.3-di-changes.html
+/frontend/encore/shared-entry /frontend/encore/split-chunks
+/frontend/encore/page-specific-assets /frontend/encore/simple-example#page-specific-javascript-or-css
+/testing/functional_tests_assertions /testing#testing-application-assertions
+/components https://symfony.com/components
+/components/index https://symfony.com/components
+/serializer/normalizers /serializer#serializer-built-in-normalizers
+/logging/monolog_regex_based_excludes /logging/monolog_exclude_http_codes
+/security/named_encoders /security/named_hashers
+/components/inflector /string#inflector
+/security/experimental_authenticators /security
+/security/user_provider /security/user_providers
+/security/reset_password /security/passwords#reset-password
+/security/auth_providers /security#security-authenticators
+/security/form_login /security#form-login
+/security/form_login_setup /security#form-login
+/security/json_login_setup /security#json-login
+/security/named_hashers /security/passwords#named-password-hashers
+/security/password_migration /security/passwords#security-password-migration
+/security/acl https://github.com/symfony/acl-bundle/blob/main/src/Resources/doc/index.rst
+/security/securing_services /security#securing-other-services
+/security/authenticator_manager /security
+/security/multiple_guard_authenticators /security/entry_point
+/security/guard_authentication /security/custom_authenticator
+/components/security/authentication /security#authenticating-users
+/components/security/authorization /security#access-control-authorization
+/components/security/firewall /security#the-firewall
+/components/security/secure_tools /security/passwords
+/components/security /security
+/components/var_dumper/advanced /components/var_dumper#advanced-usage
+/components/yaml/yaml_format /reference/formats/yaml
+/components/expression_language/syntax /reference/formats/expression_language
+/components/expression_language/ast /components/expression_language#expression-language-ast
+/components/expression_language/caching /components/expression_language#expression-language-caching
+/components/expression_language/extending /components/expression_language#expression-language-extending
+/notifier/chatters /notifier#sending-chat-messages
+/notifier/texters /notifier#sending-sms
+/notifier/events /notifier#notifier-events
+/email /mailer
+/frontend/assetic /frontend
+/frontend/assetic/index /frontend
+/controller/argument_value_resolver /controller/value_resolver
+/frontend/ux https://symfony.com/bundles/StimulusBundle/current/index.html
+/messenger/handler_results /messenger#messenger-getting-handler-results
+/messenger/dispatch_after_current_bus /messenger#messenger-transactional-messages
+/messenger/multiple_buses /messenger#messenger-multiple-buses
+/frontend/encore/server-data /frontend/server-data
+/components/string /string
+/testing/http_authentication /testing#testing_logging_in_users
+/doctrine/registration_form /security#security-make-registration-form
+/form/form_dependencies /form/create_custom_field_type
+/doctrine/reverse_engineering /doctrine#doctrine-adding-mapping
+/components/serializer /serializer
+/serializer/custom_encoder /serializer/encoders#serializer-custom-encoder
diff --git a/_images/components/console/completion.gif b/_images/components/console/completion.gif
new file mode 100644
index 00000000000..18b3f5475c8
Binary files /dev/null and b/_images/components/console/completion.gif differ
diff --git a/_images/components/console/cursor.gif b/_images/components/console/cursor.gif
new file mode 100644
index 00000000000..71a74dd8637
Binary files /dev/null and b/_images/components/console/cursor.gif differ
diff --git a/_images/components/console/debug_formatter.png b/_images/components/console/debug_formatter.png
index 7482f39851f..4ba2c0c2b57 100644
Binary files a/_images/components/console/debug_formatter.png and b/_images/components/console/debug_formatter.png differ
diff --git a/_images/components/console/process-helper-debug.png b/_images/components/console/process-helper-debug.png
index 282e1336389..96c5c316739 100644
Binary files a/_images/components/console/process-helper-debug.png and b/_images/components/console/process-helper-debug.png differ
diff --git a/_images/components/console/process-helper-error-debug.png b/_images/components/console/process-helper-error-debug.png
index 8d1145478f2..48f6c7258d4 100644
Binary files a/_images/components/console/process-helper-error-debug.png and b/_images/components/console/process-helper-error-debug.png differ
diff --git a/_images/components/console/process-helper-verbose.png b/_images/components/console/process-helper-verbose.png
index c4c912e1433..abdff9812b0 100644
Binary files a/_images/components/console/process-helper-verbose.png and b/_images/components/console/process-helper-verbose.png differ
diff --git a/_images/components/console/progress.png b/_images/components/console/progress.png
deleted file mode 100644
index c126bff5252..00000000000
Binary files a/_images/components/console/progress.png and /dev/null differ
diff --git a/_images/components/console/progressbar.gif b/_images/components/console/progressbar.gif
index 6c80e6e897f..0746e399354 100644
Binary files a/_images/components/console/progressbar.gif and b/_images/components/console/progressbar.gif differ
diff --git a/_images/components/console/table.png b/_images/components/console/table.png
deleted file mode 100644
index ba1e3ae79b9..00000000000
Binary files a/_images/components/console/table.png and /dev/null differ
diff --git a/_images/components/form/general_flow.png b/_images/components/form/general_flow.png
deleted file mode 100644
index 31650e52af6..00000000000
Binary files a/_images/components/form/general_flow.png and /dev/null differ
diff --git a/_images/components/form/set_data_flow.png b/_images/components/form/set_data_flow.png
deleted file mode 100644
index 3cd4b1e2f7b..00000000000
Binary files a/_images/components/form/set_data_flow.png and /dev/null differ
diff --git a/_images/components/form/submission_flow.png b/_images/components/form/submission_flow.png
deleted file mode 100644
index a3c6e9cfb90..00000000000
Binary files a/_images/components/form/submission_flow.png and /dev/null differ
diff --git a/_images/components/http_kernel/01-workflow.png b/_images/components/http_kernel/01-workflow.png
deleted file mode 100644
index 78c155904ca..00000000000
Binary files a/_images/components/http_kernel/01-workflow.png and /dev/null differ
diff --git a/_images/components/http_kernel/02-kernel-request.png b/_images/components/http_kernel/02-kernel-request.png
deleted file mode 100644
index b90aa35a3f3..00000000000
Binary files a/_images/components/http_kernel/02-kernel-request.png and /dev/null differ
diff --git a/_images/components/http_kernel/03-kernel-request-response.png b/_images/components/http_kernel/03-kernel-request-response.png
deleted file mode 100644
index 5f4c4a188c4..00000000000
Binary files a/_images/components/http_kernel/03-kernel-request-response.png and /dev/null differ
diff --git a/_images/components/http_kernel/04-resolve-controller.png b/_images/components/http_kernel/04-resolve-controller.png
deleted file mode 100644
index 6283558ebd1..00000000000
Binary files a/_images/components/http_kernel/04-resolve-controller.png and /dev/null differ
diff --git a/_images/components/http_kernel/06-kernel-controller.png b/_images/components/http_kernel/06-kernel-controller.png
deleted file mode 100644
index 95f1bcc4286..00000000000
Binary files a/_images/components/http_kernel/06-kernel-controller.png and /dev/null differ
diff --git a/_images/components/http_kernel/07-controller-arguments.png b/_images/components/http_kernel/07-controller-arguments.png
deleted file mode 100644
index 66cdaa247eb..00000000000
Binary files a/_images/components/http_kernel/07-controller-arguments.png and /dev/null differ
diff --git a/_images/components/http_kernel/08-call-controller.png b/_images/components/http_kernel/08-call-controller.png
deleted file mode 100644
index 877272b7c7c..00000000000
Binary files a/_images/components/http_kernel/08-call-controller.png and /dev/null differ
diff --git a/_images/components/http_kernel/09-controller-returns-response.png b/_images/components/http_kernel/09-controller-returns-response.png
deleted file mode 100644
index ae0d1d1d20a..00000000000
Binary files a/_images/components/http_kernel/09-controller-returns-response.png and /dev/null differ
diff --git a/_images/components/http_kernel/10-kernel-view.png b/_images/components/http_kernel/10-kernel-view.png
deleted file mode 100644
index cdf3329e65a..00000000000
Binary files a/_images/components/http_kernel/10-kernel-view.png and /dev/null differ
diff --git a/_images/components/http_kernel/11-kernel-exception.png b/_images/components/http_kernel/11-kernel-exception.png
deleted file mode 100644
index c380758e400..00000000000
Binary files a/_images/components/http_kernel/11-kernel-exception.png and /dev/null differ
diff --git a/_images/components/http_kernel/http-workflow-exception.svg b/_images/components/http_kernel/http-workflow-exception.svg
new file mode 100644
index 00000000000..3330010367a
--- /dev/null
+++ b/_images/components/http_kernel/http-workflow-exception.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/_images/components/http_kernel/http-workflow-subrequest.svg b/_images/components/http_kernel/http-workflow-subrequest.svg
new file mode 100644
index 00000000000..4f4912dc5a1
--- /dev/null
+++ b/_images/components/http_kernel/http-workflow-subrequest.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/_images/components/http_kernel/http-workflow.svg b/_images/components/http_kernel/http-workflow.svg
new file mode 100644
index 00000000000..f3bc7a9ee8b
--- /dev/null
+++ b/_images/components/http_kernel/http-workflow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/_images/components/http_kernel/request-response-flow.png b/_images/components/http_kernel/request-response-flow.png
deleted file mode 100644
index 2ae1011a101..00000000000
Binary files a/_images/components/http_kernel/request-response-flow.png and /dev/null differ
diff --git a/_images/components/http_kernel/sub-request.png b/_images/components/http_kernel/sub-request.png
deleted file mode 100644
index a26efe7ecd1..00000000000
Binary files a/_images/components/http_kernel/sub-request.png and /dev/null differ
diff --git a/_images/components/messenger/basic_cycle.png b/_images/components/messenger/basic_cycle.png
new file mode 100644
index 00000000000..a0558968cbb
Binary files /dev/null and b/_images/components/messenger/basic_cycle.png differ
diff --git a/_images/components/messenger/overview.svg b/_images/components/messenger/overview.svg
new file mode 100644
index 00000000000..4b82c203756
--- /dev/null
+++ b/_images/components/messenger/overview.svg
@@ -0,0 +1 @@
+
diff --git a/_images/components/phpunit_bridge/report.png b/_images/components/phpunit_bridge/report.png
deleted file mode 100644
index 3a4534c1383..00000000000
Binary files a/_images/components/phpunit_bridge/report.png and /dev/null differ
diff --git a/_images/components/scheduler/generate_consume.png b/_images/components/scheduler/generate_consume.png
new file mode 100644
index 00000000000..269281266a5
Binary files /dev/null and b/_images/components/scheduler/generate_consume.png differ
diff --git a/_images/components/scheduler/scheduler_cycle.png b/_images/components/scheduler/scheduler_cycle.png
new file mode 100644
index 00000000000..18addb37d91
Binary files /dev/null and b/_images/components/scheduler/scheduler_cycle.png differ
diff --git a/_images/components/serializer/serializer_workflow.png b/_images/components/serializer/serializer_workflow.png
deleted file mode 100644
index 3e1944e6cec..00000000000
Binary files a/_images/components/serializer/serializer_workflow.png and /dev/null differ
diff --git a/_images/components/string/bytes-points-graphemes.png b/_images/components/string/bytes-points-graphemes.png
new file mode 100644
index 00000000000..18d971cecf7
Binary files /dev/null and b/_images/components/string/bytes-points-graphemes.png differ
diff --git a/_images/components/var_dumper/10-uninitialized.png b/_images/components/var_dumper/10-uninitialized.png
new file mode 100644
index 00000000000..735731b83b5
Binary files /dev/null and b/_images/components/var_dumper/10-uninitialized.png differ
diff --git a/_images/components/workflow/blogpost.png b/_images/components/workflow/blogpost.png
index 38e29250eb1..b7f51eabb43 100644
Binary files a/_images/components/workflow/blogpost.png and b/_images/components/workflow/blogpost.png differ
diff --git a/_images/components/workflow/blogpost_mermaid.png b/_images/components/workflow/blogpost_mermaid.png
new file mode 100644
index 00000000000..7a4d3a57cfe
Binary files /dev/null and b/_images/components/workflow/blogpost_mermaid.png differ
diff --git a/_images/components/workflow/blogpost_metadata.png b/_images/components/workflow/blogpost_metadata.png
new file mode 100644
index 00000000000..783f51c6ccf
Binary files /dev/null and b/_images/components/workflow/blogpost_metadata.png differ
diff --git a/_images/components/workflow/blogpost_puml.png b/_images/components/workflow/blogpost_puml.png
new file mode 100644
index 00000000000..efe543a6f8e
Binary files /dev/null and b/_images/components/workflow/blogpost_puml.png differ
diff --git a/_images/components/workflow/pull_request.png b/_images/components/workflow/pull_request.png
index 3b98078099a..692a95345ae 100644
Binary files a/_images/components/workflow/pull_request.png and b/_images/components/workflow/pull_request.png differ
diff --git a/_images/components/workflow/pull_request_puml_styled.png b/_images/components/workflow/pull_request_puml_styled.png
new file mode 100644
index 00000000000..cda9233d731
Binary files /dev/null and b/_images/components/workflow/pull_request_puml_styled.png differ
diff --git a/_images/components/workflow/states_transitions.png b/_images/components/workflow/states_transitions.png
index 1e68f9ca597..d1f54391afd 100644
Binary files a/_images/components/workflow/states_transitions.png and b/_images/components/workflow/states_transitions.png differ
diff --git a/_images/contributing/code/stack-trace.gif b/_images/contributing/code/stack-trace.gif
new file mode 100644
index 00000000000..97a2043448d
Binary files /dev/null and b/_images/contributing/code/stack-trace.gif differ
diff --git a/_images/contributing/docs-github-create-pr.png b/_images/contributing/docs-github-create-pr.png
index 29fe22f5dbd..43b6842ffc2 100644
Binary files a/_images/contributing/docs-github-create-pr.png and b/_images/contributing/docs-github-create-pr.png differ
diff --git a/_images/contributing/docs-github-edit-page.png b/_images/contributing/docs-github-edit-page.png
index c34f13f0889..b739497f70f 100644
Binary files a/_images/contributing/docs-github-edit-page.png and b/_images/contributing/docs-github-edit-page.png differ
diff --git a/_images/contributing/docs-pull-request-change-base.png b/_images/contributing/docs-pull-request-change-base.png
index d824e8ef1bc..791901b8ec6 100644
Binary files a/_images/contributing/docs-pull-request-change-base.png and b/_images/contributing/docs-pull-request-change-base.png differ
diff --git a/_images/contributing/docs-pull-request-platformsh.png b/_images/contributing/docs-pull-request-platformsh.png
deleted file mode 100644
index 30077cce94f..00000000000
Binary files a/_images/contributing/docs-pull-request-platformsh.png and /dev/null differ
diff --git a/_images/contributing/release-process.jpg b/_images/contributing/release-process.jpg
deleted file mode 100644
index 9868404b07f..00000000000
Binary files a/_images/contributing/release-process.jpg and /dev/null differ
diff --git a/_images/controller/error_pages/errors-in-prod-environment.png b/_images/controller/error_pages/errors-in-prod-environment.png
index 79fe5341b47..808d0d70028 100644
Binary files a/_images/controller/error_pages/errors-in-prod-environment.png and b/_images/controller/error_pages/errors-in-prod-environment.png differ
diff --git a/_images/controller/error_pages/exceptions-in-dev-environment.png b/_images/controller/error_pages/exceptions-in-dev-environment.png
index 225dcdfa0dc..e1fba2bebf9 100644
Binary files a/_images/controller/error_pages/exceptions-in-dev-environment.png and b/_images/controller/error_pages/exceptions-in-dev-environment.png differ
diff --git a/_images/docs-pull-request-change-base.png b/_images/docs-pull-request-change-base.png
deleted file mode 100644
index d824e8ef1bc..00000000000
Binary files a/_images/docs-pull-request-change-base.png and /dev/null differ
diff --git a/_images/doctrine/mapping_relations.png b/_images/doctrine/mapping_relations.png
deleted file mode 100644
index a679f9cb317..00000000000
Binary files a/_images/doctrine/mapping_relations.png and /dev/null differ
diff --git a/_images/doctrine/mapping_relations.svg b/_images/doctrine/mapping_relations.svg
new file mode 100644
index 00000000000..7dc8979cb1a
--- /dev/null
+++ b/_images/doctrine/mapping_relations.svg
@@ -0,0 +1,602 @@
+
+
diff --git a/_images/doctrine/mapping_relations_proxy.png b/_images/doctrine/mapping_relations_proxy.png
deleted file mode 100644
index 935153291d4..00000000000
Binary files a/_images/doctrine/mapping_relations_proxy.png and /dev/null differ
diff --git a/_images/doctrine/mapping_relations_proxy.svg b/_images/doctrine/mapping_relations_proxy.svg
new file mode 100644
index 00000000000..634d1b0add2
--- /dev/null
+++ b/_images/doctrine/mapping_relations_proxy.svg
@@ -0,0 +1,926 @@
+
+
diff --git a/_images/doctrine/mapping_single_entity.png b/_images/doctrine/mapping_single_entity.png
deleted file mode 100644
index 6f88c6cacfa..00000000000
Binary files a/_images/doctrine/mapping_single_entity.png and /dev/null differ
diff --git a/_images/doctrine/mapping_single_entity.svg b/_images/doctrine/mapping_single_entity.svg
new file mode 100644
index 00000000000..5d517c85fb1
--- /dev/null
+++ b/_images/doctrine/mapping_single_entity.svg
@@ -0,0 +1,469 @@
+
+
diff --git a/_images/form/data-transformer-types.png b/_images/form/data-transformer-types.png
deleted file mode 100644
index 950acd39ea7..00000000000
Binary files a/_images/form/data-transformer-types.png and /dev/null differ
diff --git a/_images/form/data-transformer-types.svg b/_images/form/data-transformer-types.svg
new file mode 100644
index 00000000000..9393b224f89
--- /dev/null
+++ b/_images/form/data-transformer-types.svg
@@ -0,0 +1,178 @@
+
+
diff --git a/_images/form/form-custom-type-postal-address-fragment-names.svg b/_images/form/form-custom-type-postal-address-fragment-names.svg
new file mode 100644
index 00000000000..db9463b8327
--- /dev/null
+++ b/_images/form/form-custom-type-postal-address-fragment-names.svg
@@ -0,0 +1 @@
+
diff --git a/_images/form/form-custom-type-postal-address.svg b/_images/form/form-custom-type-postal-address.svg
new file mode 100644
index 00000000000..42ffce4067f
--- /dev/null
+++ b/_images/form/form-custom-type-postal-address.svg
@@ -0,0 +1 @@
+
diff --git a/_images/form/form-field-parts.svg b/_images/form/form-field-parts.svg
new file mode 100644
index 00000000000..c9856c89a99
--- /dev/null
+++ b/_images/form/form-field-parts.svg
@@ -0,0 +1 @@
+
diff --git a/_images/form/form_prepopulation_workflow.svg b/_images/form/form_prepopulation_workflow.svg
new file mode 100644
index 00000000000..c908f5c5a76
--- /dev/null
+++ b/_images/form/form_prepopulation_workflow.svg
@@ -0,0 +1,253 @@
+
+
diff --git a/_images/form/form_submission_workflow.svg b/_images/form/form_submission_workflow.svg
new file mode 100644
index 00000000000..d6d138ee61a
--- /dev/null
+++ b/_images/form/form_submission_workflow.svg
@@ -0,0 +1,334 @@
+
+
diff --git a/_images/form/form_workflow.svg b/_images/form/form_workflow.svg
new file mode 100644
index 00000000000..2dbacbbf096
--- /dev/null
+++ b/_images/form/form_workflow.svg
@@ -0,0 +1,263 @@
+
+
diff --git a/_images/form/simple-form.png b/_images/form/simple-form.png
index 9de28ddc4f8..1dced444561 100644
Binary files a/_images/form/simple-form.png and b/_images/form/simple-form.png differ
diff --git a/_images/form/tailwindcss-form.png b/_images/form/tailwindcss-form.png
new file mode 100644
index 00000000000..8a290749149
Binary files /dev/null and b/_images/form/tailwindcss-form.png differ
diff --git a/_images/http/request-flow.png b/_images/http/request-flow.png
deleted file mode 100644
index cbf4019307b..00000000000
Binary files a/_images/http/request-flow.png and /dev/null differ
diff --git a/_images/http/request-flow.svg b/_images/http/request-flow.svg
new file mode 100644
index 00000000000..97061ada0d5
--- /dev/null
+++ b/_images/http/request-flow.svg
@@ -0,0 +1 @@
+
diff --git a/_images/http/xkcd-full.png b/_images/http/xkcd-full.png
deleted file mode 100644
index 58edf13f3f3..00000000000
Binary files a/_images/http/xkcd-full.png and /dev/null differ
diff --git a/_images/http/xkcd-full.svg b/_images/http/xkcd-full.svg
new file mode 100644
index 00000000000..da590c2b97e
--- /dev/null
+++ b/_images/http/xkcd-full.svg
@@ -0,0 +1,324 @@
+
+
diff --git a/_images/http/xkcd-request.png b/_images/http/xkcd-request.png
deleted file mode 100644
index 86e767db9b5..00000000000
Binary files a/_images/http/xkcd-request.png and /dev/null differ
diff --git a/_images/http/xkcd-request.svg b/_images/http/xkcd-request.svg
new file mode 100644
index 00000000000..6a21280ca34
--- /dev/null
+++ b/_images/http/xkcd-request.svg
@@ -0,0 +1,191 @@
+
+
diff --git a/_images/install/deprecations-in-profiler.png b/_images/install/deprecations-in-profiler.png
index 7b32d59cb05..3d3f9a98a4a 100644
Binary files a/_images/install/deprecations-in-profiler.png and b/_images/install/deprecations-in-profiler.png differ
diff --git a/_images/mercure/chrome.png b/_images/mercure/chrome.png
new file mode 100644
index 00000000000..8ccc55a0a88
Binary files /dev/null and b/_images/mercure/chrome.png differ
diff --git a/_images/mercure/discovery.svg b/_images/mercure/discovery.svg
new file mode 100644
index 00000000000..ed18381068a
--- /dev/null
+++ b/_images/mercure/discovery.svg
@@ -0,0 +1,294 @@
+
+
diff --git a/_images/mercure/hub.svg b/_images/mercure/hub.svg
new file mode 100644
index 00000000000..6b5e496e3c6
--- /dev/null
+++ b/_images/mercure/hub.svg
@@ -0,0 +1,196 @@
+
+
diff --git a/_images/mercure/panel.png b/_images/mercure/panel.png
new file mode 100644
index 00000000000..22b214f5ff2
Binary files /dev/null and b/_images/mercure/panel.png differ
diff --git a/_images/notifier/microsoft_teams/message-card.png b/_images/notifier/microsoft_teams/message-card.png
new file mode 100644
index 00000000000..05f505fb3e0
Binary files /dev/null and b/_images/notifier/microsoft_teams/message-card.png differ
diff --git a/_images/notifier/microsoft_teams/message.png b/_images/notifier/microsoft_teams/message.png
new file mode 100644
index 00000000000..5c4c7f11ed1
Binary files /dev/null and b/_images/notifier/microsoft_teams/message.png differ
diff --git a/_images/notifier/slack/field-method.png b/_images/notifier/slack/field-method.png
new file mode 100644
index 00000000000..d77a60e6a2e
Binary files /dev/null and b/_images/notifier/slack/field-method.png differ
diff --git a/_images/notifier/slack/message-reply.png b/_images/notifier/slack/message-reply.png
new file mode 100644
index 00000000000..9a60e4573ab
Binary files /dev/null and b/_images/notifier/slack/message-reply.png differ
diff --git a/_images/notifier/slack/slack-footer.png b/_images/notifier/slack/slack-footer.png
new file mode 100644
index 00000000000..a53952c78f6
Binary files /dev/null and b/_images/notifier/slack/slack-footer.png differ
diff --git a/_images/notifier/slack/slack-header.png b/_images/notifier/slack/slack-header.png
new file mode 100644
index 00000000000..a7caf915d8f
Binary files /dev/null and b/_images/notifier/slack/slack-header.png differ
diff --git a/_images/profiler/web-interface.png b/_images/profiler/web-interface.png
new file mode 100644
index 00000000000..b107f6427d7
Binary files /dev/null and b/_images/profiler/web-interface.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..030953a17b1
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
deleted file mode 100644
index 72cd7483f2f..00000000000
Binary files a/_images/quick_tour/web_debug_toolbar.png and /dev/null 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/rate_limiter/fixed_window.svg b/_images/rate_limiter/fixed_window.svg
new file mode 100644
index 00000000000..83d5f6e79ac
--- /dev/null
+++ b/_images/rate_limiter/fixed_window.svg
@@ -0,0 +1,84 @@
+
diff --git a/_images/rate_limiter/sliding_window.svg b/_images/rate_limiter/sliding_window.svg
new file mode 100644
index 00000000000..2c565615441
--- /dev/null
+++ b/_images/rate_limiter/sliding_window.svg
@@ -0,0 +1,65 @@
+
diff --git a/_images/rate_limiter/token_bucket.svg b/_images/rate_limiter/token_bucket.svg
new file mode 100644
index 00000000000..29d6fc8f103
--- /dev/null
+++ b/_images/rate_limiter/token_bucket.svg
@@ -0,0 +1,83 @@
+
diff --git a/_images/release-process.jpg b/_images/release-process.jpg
deleted file mode 100644
index 9868404b07f..00000000000
Binary files a/_images/release-process.jpg and /dev/null differ
diff --git a/_images/security/anonymous_wdt.png b/_images/security/anonymous_wdt.png
index 8dbf1cd8298..80736afce39 100644
Binary files a/_images/security/anonymous_wdt.png and b/_images/security/anonymous_wdt.png differ
diff --git a/_images/security/authentication-guard-methods.png b/_images/security/authentication-guard-methods.png
deleted file mode 100644
index c6f81ed0e12..00000000000
Binary files a/_images/security/authentication-guard-methods.png and /dev/null differ
diff --git a/_images/security/authentication-guard-methods.svg b/_images/security/authentication-guard-methods.svg
new file mode 100644
index 00000000000..cc042656212
--- /dev/null
+++ b/_images/security/authentication-guard-methods.svg
@@ -0,0 +1 @@
+
diff --git a/_images/security/http_basic_popup.png b/_images/security/http_basic_popup.png
deleted file mode 100644
index fcd9a4ed836..00000000000
Binary files a/_images/security/http_basic_popup.png and /dev/null differ
diff --git a/_images/security/login_link_email.png b/_images/security/login_link_email.png
new file mode 100644
index 00000000000..8331b878f68
Binary files /dev/null and b/_images/security/login_link_email.png differ
diff --git a/_images/security/profiler-badges.png b/_images/security/profiler-badges.png
new file mode 100644
index 00000000000..a19f8539581
Binary files /dev/null and b/_images/security/profiler-badges.png differ
diff --git a/_images/security/security_events.svg b/_images/security/security_events.svg
new file mode 100644
index 00000000000..f1b93923da6
--- /dev/null
+++ b/_images/security/security_events.svg
@@ -0,0 +1,338 @@
+
+
diff --git a/_images/security/symfony_loggedin_wdt.png b/_images/security/symfony_loggedin_wdt.png
index ca182192c94..b51e1cafba1 100644
Binary files a/_images/security/symfony_loggedin_wdt.png and b/_images/security/symfony_loggedin_wdt.png differ
diff --git a/_images/serializer/serializer_workflow.svg b/_images/serializer/serializer_workflow.svg
new file mode 100644
index 00000000000..b6e9c254778
--- /dev/null
+++ b/_images/serializer/serializer_workflow.svg
@@ -0,0 +1,283 @@
+
+
diff --git a/_images/sources/README.md b/_images/sources/README.md
new file mode 100644
index 00000000000..84810a9783d
--- /dev/null
+++ b/_images/sources/README.md
@@ -0,0 +1,102 @@
+How to Create Symfony Images
+============================
+
+Creating Diagrams
+-----------------
+
+* Use [Dia][1] as the diagramming application;
+* Use [PT Sans Narrow][2] as the only font in all diagrams (if possible, use
+ only the "normal" weight for all contents);
+* Use 36pt as the base font size;
+* Use 0.10 cm width for lines and shape borders;
+* Use the following color palette:
+ * Text, lines and shape borders: black (#000000)
+ * Shape backgrounds:
+ * Grays: dark (#4d4d4d), medium (#b3b3b3), light (#f2f2f2)
+ * Blue: #b2d4eb
+ * Red: #ecbec0
+ * Green: #b2dec7
+ * Orange: #fddfbb
+
+In case of doubt, check the existing diagrams or ask to the
+[Symfony Documentation Team][3].
+
+### Saving and Exporting the Diagram
+
+* Save the original diagram in `*.dia` format in `_images/sources/`;
+* Export the diagram to SVG format and save it in `_images/`.
+
+Important: choose "Cairo Scalable Vector Graphics (.svg)" format instead of
+plain " Scalable Vector Graphics (.svg)" because the former is the only format
+that transforms text into vector shapes (resulting file is larger in size, but
+it's truly portable because text is displayed the same even if you don't have
+some fonts installed).
+
+### Including the Diagram in the Symfony Docs
+
+Use the following snippet to embed the diagram in the docs:
+
+```
+.. raw:: html
+
+
+```
+
+### Reasoning
+
+* Dia was chosen because it's one of the few applications which are free, open
+ source and compatible with Linux, macOS and Windows.
+* Font, colors and line widths were chosen to be similar to the diagrams used
+ in the best tech books.
+
+### Troubleshooting
+
+* On some macOS systems, Dia cannot be executed as a regular application and
+ you must run the following console command instead:
+ `export DISPLAY=:0 && /Applications/Dia.app/Contents/Resources/bin/dia`
+
+Creating Console Screenshots
+----------------------------
+
+* Use [Asciinema][4] to record the console session locally:
+
+ ```
+ $ asciinema rec -c bash recording.cast
+ ```
+* Use `$ ` as the prompt in recordings. E.g. if you're using Bash, add the
+ following lines to your ``.bashrc``:
+
+ ```
+ if [ "$ASCIINEMA_REC" = "1" ]; then
+ PS1="\e[37m$ \e[0m"
+ fi
+ ```
+* Save the generated asciicast in `_images/sources/`.
+
+### Rendering the Recording
+
+Rendering the recording can be a difficult task. The [documentation team][3]
+is always ready to help you with this task (e.g. you can open a PR with
+only the asciicast file).
+
+* Use [agg][5] to generated a GIF file from the recording;
+* Install the [JetBrains Mono][6] font;
+* Use the ``_images/sources/ascii-render.sh`` file to call agg:
+
+ ```
+ AGG_PATH=/path/to/agg ./_images/sources/ascii-render.sh recording.cast --cols 45 --rows 20
+ ```
+
+ This utility configures a predefined theme;
+* Always configure `--cols`` (width) and ``--rows`` (height), try to use as
+ low as possible numbers. Do not exceed 70 columns;
+* Save the generated GIF file in `_images/`.
+
+[1]: http://dia-installer.de/
+[2]: https://fonts.google.com/specimen/PT+Sans+Narrow
+[3]: https://symfony.com/doc/current/contributing/core_team.html
+[4]: https://github.com/asciinema/asciinema
+[5]: https://github.com/asciinema/agg
+[6]: https://www.jetbrains.com/lp/mono/
diff --git a/_images/sources/ascii-render.sh b/_images/sources/ascii-render.sh
new file mode 100755
index 00000000000..e72be572390
--- /dev/null
+++ b/_images/sources/ascii-render.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env sh
+case "$1" in
+ ''|help|-h)
+ echo "ansi-render.sh RECORDING [options]"
+ echo ""
+ echo " RECORDING: path to the .cast file generated by asciinema"
+ echo " [options]: optional options to be passed to agg"
+ ;;
+ *)
+ recording=$1
+ extra_options=
+ if [ $# -gt 1 ]; then
+ shift
+ extra_options=$@
+ fi
+
+ # optionally, use this green color: 1f4631
+ ${AGG_PATH:-agg} \
+ --theme 18202a,f9fafb,f9fafb,ff7b72,7ee787,ffa657,79c0ff,d2a8ff,a5d6ff,f9fafb,8b949e,ff7b72,00c300,ffa657,79c0ff,d2a8ff,a5d6ff,f9fafb --line-height 1.6 \
+ --font-family 'JetBrains Mono' \
+ $extra_options \
+ $recording $(echo $recording | sed "s/cast/gif/")
+ ;;
+esac
diff --git a/_images/sources/components/console/completion.cast b/_images/sources/components/console/completion.cast
new file mode 100644
index 00000000000..c268863e9b0
--- /dev/null
+++ b/_images/sources/components/console/completion.cast
@@ -0,0 +1,37 @@
+{"version": 2, "width": 76, "height": 30, "timestamp": 1663253713, "env": {"SHELL": "/usr/bin/fish", "TERM": "st-256color"}}
+[0.00798, "o", "\u001b[?2004h\u001b[90m$ \u001b[0m"]
+[0.614685, "o", "b"]
+[0.776549, "o", "i"]
+[0.86682, "o", "n"]
+[1.092426, "o", "/"]
+[1.332671, "o", "c"]
+[1.55068, "o", "o"]
+[1.630651, "o", "n"]
+[1.784584, "o", "s"]
+[1.873108, "o", "o"]
+[2.074652, "o", "l"]
+[2.180433, "o", "e"]
+[2.260475, "o", " "]
+[2.696628, "o", "\u0007"]
+[2.947263, "o", "\r\nabout debug:event-dispatcher\r\nassets:install debug:router\r\ncache:clear help\r\ncache:pool:clear lint:container\r\ncache:pool:delete lint:yaml\r\ncache:pool:list list\r\ncache:pool:prune router:match\r\ncache:warmup secrets:decrypt-to-local\r\ncompletion secrets:encrypt-from-local\r\nconfig:dump-reference secrets:generate-keys\r\ndebug:autowiring secrets:list\r\ndebug:config secrets:remove\r\ndebug:container secrets:set\r\ndebug:dotenv \r\n\u001b[37m$ \u001b[0mbin/console "]
+[3.614479, "o", "s"]
+[3.802449, "o", "e"]
+[4.205631, "o", "\u0007crets:"]
+[4.520435, "o", "r"]
+[4.598031, "o", "e"]
+[5.026287, "o", "move "]
+[5.47041, "o", "\u0007SOME_"]
+[5.673941, "o", "\u0007"]
+[6.024086, "o", "\r\nSOME_OTHER_SECRET SOME_SECRET \r\n\u001b[37m$ \u001b[0mbin/console secrets:remove SOME_"]
+[6.770627, "o", "O"]
+[7.14335, "o", "THER_SECRET "]
+[7.724482, "o", "\r\n\u001b[?2004l\r"]
+[7.776657, "o", "\r\n"]
+[7.779108, "o", "\u001b[30;42m \u001b[39;49m\r\n\u001b[30;42m [OK] Secret \"SOME_OTHER_SECRET\" removed from \"config/secrets/dev/\". \u001b[39;49m\r\n\u001b[30;42m \u001b[39;49m\r\n\r\n"]
+[7.782993, "o", "\u001b[?2004h\u001b[37m$ \u001b[0m"]
+[9.214537, "o", "e"]
+[9.522429, "o", "x"]
+[9.690371, "o", "i"]
+[9.85446, "o", "t"]
+[10.292412, "o", "\r\n\u001b[?2004l\r"]
+[10.292526, "o", "exit\r\n"]
diff --git a/_images/sources/components/console/cursor.cast b/_images/sources/components/console/cursor.cast
new file mode 100644
index 00000000000..be2f2f6c351
--- /dev/null
+++ b/_images/sources/components/console/cursor.cast
@@ -0,0 +1,49 @@
+{"version": 2, "width": 191, "height": 30, "timestamp": 1663251833, "env": {"SHELL": "/usr/bin/fish", "TERM": "st-256color"}}
+[0.007941, "o", "\u001b[?2004h\u001b[90m$ \u001b[0m"]
+[0.566363, "o", "c"]
+[0.643353, "o", "l"]
+[0.762325, "o", "e"]
+[0.952363, "o", "a"]
+[0.995878, "o", "r"]
+[1.107784, "o", "\r\n\u001b[?2004l\r"]
+[1.109766, "o", "\u001b[H\u001b[2J"]
+[1.109946, "o", "\u001b[?2004h\u001b[30m$ \u001b[0m"]
+[1.653461, "o", "p"]
+[1.772323, "o", "h"]
+[1.856444, "o", "p"]
+[1.980339, "o", " "]
+[2.15827, "o", "c"]
+[2.273242, "o", "u"]
+[2.402231, "o", "r"]
+[2.563066, "o", "s"]
+[2.760266, "o", "o"]
+[2.900252, "o", "r"]
+[3.020537, "o", "."]
+[3.316404, "o", "p"]
+[3.403213, "o", "h"]
+[3.483391, "o", "p"]
+[3.820273, "o", "\r\n\u001b[?2004l\r"]
+[3.845697, "o", "\u001b[6;9H#"]
+[4.045942, "o", "\u001b[8;9H#"]
+[4.246327, "o", "\u001b[8;2H#####"]
+[4.446737, "o", "\u001b[2;9H#######"]
+[4.647128, "o", "\u001b[7;7H#"]
+[4.84749, "o", "\u001b[3;9H#"]
+[5.047857, "o", "\u001b[7;9H#"]
+[5.248246, "o", "\u001b[4;9H#"]
+[5.448622, "o", "\u001b[2;2H#####"]
+[5.648999, "o", "\u001b[3;7H#"]
+[5.849378, "o", "\u001b[5;9H#####"]
+[6.049711, "o", "\u001b[3;1H#"]
+[6.250118, "o", "\u001b[7;1H#"]
+[6.45056, "o", "\u001b[5;2H#####"]
+[6.650897, "o", "\u001b[4;1H#"]
+[6.851281, "o", "\u001b[6;7H#"]
+[7.051644, "o", "\u001b[9;1H"]
+[7.058802, "o", "\u001b[?2004h\u001b[30m$ \u001b[0m"]
+[7.657612, "o", "e"]
+[7.846956, "o", "x"]
+[7.949451, "o", "i"]
+[8.0893, "o", "t"]
+[8.201144, "o", "\r\n\u001b[?2004l\r"]
+[8.201227, "o", "exit\r\n"]
diff --git a/_images/sources/components/console/progress.cast b/_images/sources/components/console/progress.cast
new file mode 100644
index 00000000000..9c5244b37e2
--- /dev/null
+++ b/_images/sources/components/console/progress.cast
@@ -0,0 +1,57 @@
+{"version": 2, "width": 191, "height": 17, "timestamp": 1663423221, "env": {"SHELL": "/usr/bin/fish", "TERM": "st-256color"}}
+[0.008171, "o", "\u001b[?2004h\u001b[90m$ \u001b[0m"]
+[0.385858, "o", "p"]
+[0.577979, "o", "h"]
+[0.768282, "o", "p"]
+[0.96433, "o", " "]
+[1.133645, "o", "p"]
+[1.262693, "o", "r"]
+[1.385832, "o", "o"]
+[1.476876, "o", "g"]
+[1.652322, "o", "r"]
+[1.722357, "o", "e"]
+[1.935395, "o", "s"]
+[2.083915, "o", "s"]
+[2.200109, "o", "."]
+[2.403686, "o", "p"]
+[2.510201, "o", "h"]
+[2.602756, "o", "p"]
+[2.909974, "o", "\r\n\u001b[?2004l\r"]
+[2.935647, "o", "\u001b[34m Starting the demo... fingers crossed \u001b[39m\r\n 0/15 \u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 0%\r\n < 1 sec 4.0 MiB"]
+[3.418022, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[3.419196, "o", "\u001b[34m Starting the demo... fingers crossed \u001b[39m\r\n 2/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 13%\r\n < 1 sec 6.0 MiB"]
+[3.66102, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G"]
+[3.661071, "o", "\u001b[2K"]
+[3.661731, "o", "\u001b[34m Starting the demo... fingers crossed \u001b[39m\r\n 3/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 20%\r\n 5 secs 6.0 MiB"]
+[4.143554, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[4.14385, "o", "\u001b[34m Starting the demo... fingers crossed \u001b[39m\r\n 5/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 33%\r\n 3 secs 6.5 MiB"]
+[4.385367, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[4.38612, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n 6/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 40%\r\n 3 secs 7.1 MiB"]
+[4.868053, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[4.86852, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n 8/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 53%\r\n 4 secs 8.1 MiB"]
+[5.110341, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[5.11133, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n 9/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 60%\r\n 3 secs 8.6 MiB"]
+[5.593851, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G"]
+[5.593924, "o", "\u001b[2K"]
+[5.594818, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n11/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 73%\r\n 4 secs 9.6 MiB"]
+[5.836301, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[5.836831, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n12/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m\u001b[41m \u001b[49m 80%\r\n 4 secs 10.1 MiB"]
+[6.31877, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K\u001b[1A"]
+[6.318814, "o", "\u001b[1G\u001b[2K"]
+[6.319403, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n14/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[32;41m\u001b[39;49m\u001b[41m \u001b[49m 93%\r\n 3 secs 11.1 MiB"]
+[6.561359, "o", "\u001b[1G\u001b[2K\u001b[1A"]
+[6.561561, "o", "\u001b[1G\u001b[2K\u001b[1A\u001b[1G\u001b[2K"]
+[6.562504, "o", "\u001b[34m Looks good to me... \u001b[39m\r\n15/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m 100%\r\n 4 secs 11.6 MiB"]
+[6.563772, "o", "\u001b[1G"]
+[6.563824, "o", "\u001b[2K\u001b[1A"]
+[6.563875, "o", "\u001b[1G\u001b[2K"]
+[6.563926, "o", "\u001b[1A\u001b[1G\u001b[2K"]
+[6.564766, "o", "\u001b[34m Thanks bye! \u001b[39m\r\n15/15 \u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m\u001b[42m \u001b[49m 100%\r\n 4 secs 11.6 MiB"]
+[6.564805, "o", "\r\n\r\n"]
+[6.570516, "o", "\u001b[?2004h"]
+[6.570537, "o", "\u001b[90m$ \u001b[0m"]
+[8.441927, "o", "e"]
+[8.646449, "o", "x"]
+[8.76668, "o", "i"]
+[8.897799, "o", "t"]
+[9.091614, "o", "\r\n\u001b[?2004l\rexit\r\n"]
diff --git a/_images/sources/components/messenger/overview.dia b/_images/sources/components/messenger/overview.dia
new file mode 100644
index 00000000000..b0e2edaeab2
Binary files /dev/null and b/_images/sources/components/messenger/overview.dia differ
diff --git a/_images/sources/doctrine/mapping_relations.dia b/_images/sources/doctrine/mapping_relations.dia
new file mode 100644
index 00000000000..5703e1b781c
Binary files /dev/null and b/_images/sources/doctrine/mapping_relations.dia differ
diff --git a/_images/sources/doctrine/mapping_relations_proxy.dia b/_images/sources/doctrine/mapping_relations_proxy.dia
new file mode 100644
index 00000000000..1f491e9e2ef
Binary files /dev/null and b/_images/sources/doctrine/mapping_relations_proxy.dia differ
diff --git a/_images/sources/doctrine/mapping_single_entity.dia b/_images/sources/doctrine/mapping_single_entity.dia
new file mode 100644
index 00000000000..5a9dc21889c
Binary files /dev/null and b/_images/sources/doctrine/mapping_single_entity.dia differ
diff --git a/_images/sources/form/data-transformer-types.dia b/_images/sources/form/data-transformer-types.dia
new file mode 100644
index 00000000000..972b973a36d
Binary files /dev/null and b/_images/sources/form/data-transformer-types.dia differ
diff --git a/_images/sources/form/form-custom-type-postal-address-fragment-names.dia b/_images/sources/form/form-custom-type-postal-address-fragment-names.dia
new file mode 100644
index 00000000000..ca12fcdeadc
Binary files /dev/null and b/_images/sources/form/form-custom-type-postal-address-fragment-names.dia differ
diff --git a/_images/sources/form/form-custom-type-postal-address.dia b/_images/sources/form/form-custom-type-postal-address.dia
new file mode 100644
index 00000000000..1b7c6226315
Binary files /dev/null and b/_images/sources/form/form-custom-type-postal-address.dia differ
diff --git a/_images/sources/form/form-field-parts.dia b/_images/sources/form/form-field-parts.dia
new file mode 100644
index 00000000000..d6ed2dfc3fe
Binary files /dev/null and b/_images/sources/form/form-field-parts.dia differ
diff --git a/_images/sources/form/form_events.dia b/_images/sources/form/form_events.dia
new file mode 100644
index 00000000000..8e7afb1cb83
Binary files /dev/null and b/_images/sources/form/form_events.dia differ
diff --git a/_images/sources/form/form_prepopulation_workflow.dia b/_images/sources/form/form_prepopulation_workflow.dia
new file mode 100644
index 00000000000..1d6d450fed1
Binary files /dev/null and b/_images/sources/form/form_prepopulation_workflow.dia differ
diff --git a/_images/sources/form/form_submission_workflow.dia b/_images/sources/form/form_submission_workflow.dia
new file mode 100644
index 00000000000..cc08f117878
Binary files /dev/null and b/_images/sources/form/form_submission_workflow.dia differ
diff --git a/_images/sources/form/form_workflow.dia b/_images/sources/form/form_workflow.dia
new file mode 100644
index 00000000000..30f9acabe2b
Binary files /dev/null and b/_images/sources/form/form_workflow.dia differ
diff --git a/_images/sources/http/request-flow.dia b/_images/sources/http/request-flow.dia
new file mode 100644
index 00000000000..ca09a05504e
Binary files /dev/null and b/_images/sources/http/request-flow.dia differ
diff --git a/_images/sources/http/xkcd-full.dia b/_images/sources/http/xkcd-full.dia
new file mode 100644
index 00000000000..a730d01c3ef
Binary files /dev/null and b/_images/sources/http/xkcd-full.dia differ
diff --git a/_images/sources/http/xkcd-request.dia b/_images/sources/http/xkcd-request.dia
new file mode 100644
index 00000000000..3796228bf1d
Binary files /dev/null and b/_images/sources/http/xkcd-request.dia differ
diff --git a/_images/sources/http_kernel/http-workflow.dia b/_images/sources/http_kernel/http-workflow.dia
new file mode 100644
index 00000000000..2b84bc46aec
Binary files /dev/null and b/_images/sources/http_kernel/http-workflow.dia differ
diff --git a/_images/sources/mercure/discovery.dia b/_images/sources/mercure/discovery.dia
new file mode 100644
index 00000000000..3db5c86f020
Binary files /dev/null and b/_images/sources/mercure/discovery.dia differ
diff --git a/_images/sources/mercure/hub.dia b/_images/sources/mercure/hub.dia
new file mode 100644
index 00000000000..b0dfb9d88d2
Binary files /dev/null and b/_images/sources/mercure/hub.dia differ
diff --git a/_images/sources/rate_limiter/fixed_window.dia b/_images/sources/rate_limiter/fixed_window.dia
new file mode 100644
index 00000000000..16282a2dcce
Binary files /dev/null and b/_images/sources/rate_limiter/fixed_window.dia differ
diff --git a/_images/sources/rate_limiter/sliding_window.dia b/_images/sources/rate_limiter/sliding_window.dia
new file mode 100644
index 00000000000..e16275d8995
Binary files /dev/null and b/_images/sources/rate_limiter/sliding_window.dia differ
diff --git a/_images/sources/rate_limiter/token_bucket.dia b/_images/sources/rate_limiter/token_bucket.dia
new file mode 100644
index 00000000000..16761971337
Binary files /dev/null and b/_images/sources/rate_limiter/token_bucket.dia differ
diff --git a/_images/sources/security/authentication-guard-methods.dia b/_images/sources/security/authentication-guard-methods.dia
new file mode 100644
index 00000000000..d655be780fe
Binary files /dev/null and b/_images/sources/security/authentication-guard-methods.dia differ
diff --git a/_images/sources/security/security_events.dia b/_images/sources/security/security_events.dia
new file mode 100644
index 00000000000..0a8afa73179
Binary files /dev/null and b/_images/sources/security/security_events.dia differ
diff --git a/_images/sources/serializer/serializer_workflow.dia b/_images/sources/serializer/serializer_workflow.dia
new file mode 100644
index 00000000000..3e2ea62558f
Binary files /dev/null and b/_images/sources/serializer/serializer_workflow.dia 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/_images/translation/pseudolocalization-interface-original.png b/_images/translation/pseudolocalization-interface-original.png
new file mode 100644
index 00000000000..d89f4e63a24
Binary files /dev/null and b/_images/translation/pseudolocalization-interface-original.png differ
diff --git a/_images/translation/pseudolocalization-interface-translated.png b/_images/translation/pseudolocalization-interface-translated.png
new file mode 100644
index 00000000000..496d5a0f86f
Binary files /dev/null and b/_images/translation/pseudolocalization-interface-translated.png differ
diff --git a/_images/translation/pseudolocalization-symfony-demo-disabled.png b/_images/translation/pseudolocalization-symfony-demo-disabled.png
new file mode 100644
index 00000000000..1a7472bd41f
Binary files /dev/null and b/_images/translation/pseudolocalization-symfony-demo-disabled.png differ
diff --git a/_images/translation/pseudolocalization-symfony-demo-enabled.png b/_images/translation/pseudolocalization-symfony-demo-enabled.png
new file mode 100644
index 00000000000..a23300a7271
Binary files /dev/null and b/_images/translation/pseudolocalization-symfony-demo-enabled.png differ
diff --git a/_includes/service_container/_my_mailer.rst.inc b/_includes/service_container/_my_mailer.rst.inc
deleted file mode 100644
index 37a20edf4a9..00000000000
--- a/_includes/service_container/_my_mailer.rst.inc
+++ /dev/null
@@ -1,33 +0,0 @@
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/services.yml
- services:
- app.mailer:
- class: AppBundle\Mailer
- arguments: [sendmail]
-
- .. code-block:: xml
-
-
-
-
-
-
-
- sendmail
-
-
-
-
- .. code-block:: php
-
- // app/config/services.php
- use AppBundle\Mailer;
-
- $container->register('app.mailer', Mailer::class)
- ->addArgument('sendmail');
diff --git a/assetic.rst b/assetic.rst
deleted file mode 100644
index 59f8d06ecba..00000000000
--- a/assetic.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-Assetic
-=======
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-.. toctree::
- :maxdepth: 1
- :glob:
-
- assetic/*
diff --git a/assetic/_standard_edition_warning.rst.inc b/assetic/_standard_edition_warning.rst.inc
deleted file mode 100644
index f114bf2d43f..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/assetic/apply_to_option.rst b/assetic/apply_to_option.rst
deleted file mode 100644
index a144f08172f..00000000000
--- a/assetic/apply_to_option.rst
+++ /dev/null
@@ -1,211 +0,0 @@
-.. index::
- single: Assetic; Apply filters
-
-How to Apply an Assetic Filter to a specific File Extension
-===========================================================
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-Assetic filters can be applied to individual files, groups of files or even,
-as you'll see here, files that have a specific extension. To show you how
-to handle each option, suppose that you want to use Assetic's CoffeeScript
-filter, which compiles CoffeeScript files into JavaScript.
-
-The main configuration is just the paths to ``coffee``, ``node`` and ``node_modules``.
-An example configuration might look like this:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- coffee:
- bin: /usr/bin/coffee
- node: /usr/bin/node
- node_paths: [/usr/lib/node_modules/]
-
- .. code-block:: xml
-
-
-
-
-
-
-
- /usr/lib/node_modules/
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'coffee' => array(
- 'bin' => '/usr/bin/coffee',
- 'node' => '/usr/bin/node',
- 'node_paths' => array('/usr/lib/node_modules/'),
- ),
- ),
- ));
-
-Filter a single File
---------------------
-
-You can now serve up a single CoffeeScript file as JavaScript from within your
-templates:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/example.coffee' filter='coffee' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/example.coffee'),
- array('coffee')
- ) as $url): ?>
-
-
-
-This is all that's needed to compile this CoffeeScript file and serve it
-as the compiled JavaScript.
-
-Filter multiple Files
----------------------
-
-You can also combine multiple CoffeeScript files into a single output file:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/example.coffee'
- '@AppBundle/Resources/public/js/another.coffee'
- filter='coffee' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array(
- '@AppBundle/Resources/public/js/example.coffee',
- '@AppBundle/Resources/public/js/another.coffee',
- ),
- array('coffee')
- ) as $url): ?>
-
-
-
-Both files will now be served up as a single file compiled into regular JavaScript.
-
-.. _assetic-apply-to:
-
-Filtering Based on a File Extension
------------------------------------
-
-One of the great advantages of using Assetic is reducing the number of asset
-files to lower HTTP requests. In order to make full use of this, it would
-be good to combine *all* your JavaScript and CoffeeScript files together
-since they will ultimately all be served as JavaScript. Unfortunately just
-adding the JavaScript files to the files to be combined as above will not
-work as the regular JavaScript files will not survive the CoffeeScript compilation.
-
-This problem can be avoided by using the ``apply_to`` option, which allows you
-to specify which filter should always be applied to particular file extensions.
-In this case you can specify that the ``coffee`` filter is applied to all
-``.coffee`` files:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- coffee:
- bin: /usr/bin/coffee
- node: /usr/bin/node
- node_paths: [/usr/lib/node_modules/]
- apply_to: '\.coffee$'
-
- .. code-block:: xml
-
-
-
-
-
-
-
- /usr/lib/node_modules/
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'coffee' => array(
- 'bin' => '/usr/bin/coffee',
- 'node' => '/usr/bin/node',
- 'node_paths' => array('/usr/lib/node_modules/'),
- 'apply_to' => '\.coffee$',
- ),
- ),
- ));
-
-With this option, you no longer need to specify the ``coffee`` filter in the
-template. You can also list regular JavaScript files, all of which will be
-combined and rendered as a single JavaScript file (with only the ``.coffee``
-files being run through the CoffeeScript filter):
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/example.coffee'
- '@AppBundle/Resources/public/js/another.coffee'
- '@AppBundle/Resources/public/js/regular.js' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array(
- '@AppBundle/Resources/public/js/example.coffee',
- '@AppBundle/Resources/public/js/another.coffee',
- '@AppBundle/Resources/public/js/regular.js',
- )
- ) as $url): ?>
-
-
diff --git a/assetic/asset_management.rst b/assetic/asset_management.rst
deleted file mode 100644
index 4a78a7741b9..00000000000
--- a/assetic/asset_management.rst
+++ /dev/null
@@ -1,690 +0,0 @@
-.. index::
- single: Assetic; Introduction
-
-How to Use Assetic for Asset Management
-=======================================
-
-Installing and Enabling Assetic
--------------------------------
-
-Starting from Symfony 2.8, Assetic is no longer included by default in the
-Symfony Standard Edition. Before using any of its features, install the
-AsseticBundle executing this console command in your project:
-
-.. code-block:: bash
-
- $ composer require symfony/assetic-bundle
-
-Then, enable the bundle in the ``AppKernel.php`` file of your Symfony application::
-
- // app/AppKernel.php
-
- // ...
- class AppKernel extends Kernel
- {
- // ...
-
- public function registerBundles()
- {
- $bundles = array(
- // ...
- new Symfony\Bundle\AsseticBundle\AsseticBundle(),
- );
-
- // ...
- }
- }
-
-Finally, add the following minimal configuration to enable Assetic support in
-your application:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- debug: '%kernel.debug%'
- use_controller: '%kernel.debug%'
- filters:
- cssrewrite: ~
-
- # ...
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'debug' => '%kernel.debug%',
- 'use_controller' => '%kernel.debug%',
- 'filters' => array(
- 'cssrewrite' => null,
- ),
- // ...
- ));
-
- // ...
-
-Introducing Assetic
--------------------
-
-Assetic combines two major ideas: :ref:`assets ` and
-:ref:`filters `. The assets are files such as CSS,
-JavaScript and image files. The filters are things that can be applied to
-these files before they are served to the browser. This allows a separation
-between the asset files stored in the application and the files actually presented
-to the user.
-
-Without Assetic, you just serve the files that are stored in the application
-directly:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
-
-
- .. code-block:: php
-
-
-
-But *with* Assetic, you can manipulate these assets however you want (or
-load them from anywhere) before serving them. This means you can:
-
-* Minify and combine all of your CSS and JS files
-
-* Run all (or just some) of your CSS or JS files through some sort of compiler,
- such as LESS, SASS or CoffeeScript
-
-* Run image optimizations on your images
-
-.. _assetic-assets:
-
-Assets
-------
-
-Using Assetic provides many advantages over directly serving the files.
-The files do not need to be stored where they are served from and can be
-drawn from various sources such as from within a bundle.
-
-You can use Assetic to process :ref:`CSS stylesheets `,
-:ref:`JavaScript files ` and
-:ref:`images `. The philosophy
-behind adding either is basically the same, but with a slightly different syntax.
-
-.. _assetic-including-javascript:
-
-Including JavaScript Files
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To include JavaScript files, use the ``javascripts`` tag in any template:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*')
- ) as $url): ?>
-
-
-
-.. note::
-
- If your application templates use the default block names from the Symfony
- Standard Edition, the ``javascripts`` tag will most commonly live in the
- ``javascripts`` block:
-
- .. code-block:: html+twig
-
- {# ... #}
- {% block javascripts %}
- {% javascripts '@AppBundle/Resources/public/js/*' %}
-
- {% endjavascripts %}
- {% endblock %}
- {# ... #}
-
-.. tip::
-
- You can also include CSS stylesheets: see :ref:`assetic-including-css`.
-
-In this example, all files in the ``Resources/public/js/`` directory of the
-AppBundle will be loaded and served from a different location. The actual
-rendered tag might simply look like:
-
-.. code-block:: html
-
-
-
-This is a key point: once you let Assetic handle your assets, the files are
-served from a different location. This *will* cause problems with CSS files
-that reference images by their relative path. See :ref:`assetic-cssrewrite`.
-
-.. _assetic-including-css:
-
-Including CSS Stylesheets
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-To bring in CSS stylesheets, you can use the same technique explained above,
-except with the ``stylesheets`` tag:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% stylesheets 'bundles/app/css/*' filter='cssrewrite' %}
-
- {% endstylesheets %}
-
- .. code-block:: html+php
-
- stylesheets(
- array('bundles/app/css/*'),
- array('cssrewrite')
- ) as $url): ?>
-
-
-
-.. note::
-
- If your application templates use the default block names from the Symfony
- Standard Edition, the ``stylesheets`` tag will most commonly live in the
- ``stylesheets`` block:
-
- .. code-block:: html+twig
-
- {# ... #}
- {% block stylesheets %}
- {% stylesheets 'bundles/app/css/*' filter='cssrewrite' %}
-
- {% endstylesheets %}
- {% endblock %}
- {# ... #}
-
-But because Assetic changes the paths to your assets, this *will* break any
-background images (or other paths) that uses relative paths, unless you use
-the :ref:`cssrewrite ` filter.
-
-.. note::
-
- Notice that in the original example that included JavaScript files, you
- referred to the files using a path like ``@AppBundle/Resources/public/file.js``,
- but that in this example, you referred to the CSS files using their actual,
- publicly-accessible path: ``bundles/app/css``. You can use either, except
- that there is a known issue that causes the ``cssrewrite`` filter to fail
- when using the ``@AppBundle`` syntax for CSS stylesheets.
-
-.. _assetic-including-image:
-
-Including Images
-~~~~~~~~~~~~~~~~
-
-To include an image you can use the ``image`` tag.
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% image '@AppBundle/Resources/public/images/example.jpg' %}
-
- {% endimage %}
-
- .. code-block:: html+php
-
- image(
- array('@AppBundle/Resources/public/images/example.jpg')
- ) as $url): ?>
-
-
-
-You can also use Assetic for image optimization. More information in
-:doc:`/assetic/jpeg_optimize`.
-
-.. tip::
-
- Instead of using Assetic to include images, you may consider using the
- `LiipImagineBundle`_ community bundle, which allows to compress and
- manipulate images (rotate, resize, watermark, etc.) before serving them.
-
-.. _assetic-cssrewrite:
-
-Fixing CSS Paths with the ``cssrewrite`` Filter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Since Assetic generates new URLs for your assets, any relative paths inside
-your CSS files will break. To fix this, make sure to use the ``cssrewrite``
-filter with your ``stylesheets`` tag. This parses your CSS files and corrects
-the paths internally to reflect the new location.
-
-You can see an example in the previous section.
-
-.. caution::
-
- When using the ``cssrewrite`` filter, don't refer to your CSS files using
- the ``@AppBundle`` syntax. See the note in the above section for details.
-
-Combining Assets
-~~~~~~~~~~~~~~~~
-
-One feature of Assetic is that it will combine many files into one. This helps
-to reduce the number of HTTP requests, which is great for front-end performance.
-It also allows you to maintain the files more easily by splitting them into
-manageable parts. This can help with re-usability as you can easily split
-project-specific files from those which can be used in other applications,
-but still serve them as a single file:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts
- '@AppBundle/Resources/public/js/*'
- '@AcmeBarBundle/Resources/public/js/form.js'
- '@AcmeBarBundle/Resources/public/js/calendar.js' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array(
- '@AppBundle/Resources/public/js/*',
- '@AcmeBarBundle/Resources/public/js/form.js',
- '@AcmeBarBundle/Resources/public/js/calendar.js',
- )
- ) as $url): ?>
-
-
-
-In the ``dev`` environment, each file is still served individually, so that
-you can debug problems more easily. However, in the ``prod`` environment
-(or more specifically, when the ``debug`` flag is ``false``), this will be
-rendered as a single ``script`` tag, which contains the contents of all of
-the JavaScript files.
-
-.. tip::
-
- If you're new to Assetic and try to use your application in the ``prod``
- environment (by using the ``app.php`` controller), you'll likely see
- that all of your CSS and JS breaks. Don't worry! This is on purpose.
- For details on using Assetic in the ``prod`` environment, see :ref:`assetic-dumping`.
-
-And combining files doesn't only apply to *your* files. You can also use Assetic to
-combine third party assets, such as jQuery, with your own into a single file:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts
- '@AppBundle/Resources/public/js/thirdparty/jquery.js'
- '@AppBundle/Resources/public/js/*' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array(
- '@AppBundle/Resources/public/js/thirdparty/jquery.js',
- '@AppBundle/Resources/public/js/*',
- )
- ) as $url): ?>
-
-
-
-Using Named Assets
-~~~~~~~~~~~~~~~~~~
-
-AsseticBundle configuration directives allow you to define named asset sets.
-You can do so by defining the input files, filters and output files in your
-configuration under the ``assetic`` section. Read more in the
-:doc:`assetic config reference `.
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- assets:
- jquery_and_ui:
- inputs:
- - '@AppBundle/Resources/public/js/thirdparty/jquery.js'
- - '@AppBundle/Resources/public/js/thirdparty/jquery.ui.js'
-
- .. code-block:: xml
-
-
-
-
-
-
-
- @AppBundle/Resources/public/js/thirdparty/jquery.js
- @AppBundle/Resources/public/js/thirdparty/jquery.ui.js
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'assets' => array(
- 'jquery_and_ui' => array(
- 'inputs' => array(
- '@AppBundle/Resources/public/js/thirdparty/jquery.js',
- '@AppBundle/Resources/public/js/thirdparty/jquery.ui.js',
- ),
- ),
- ),
- ));
-
-After you have defined the named assets, you can reference them in your templates
-with the ``@named_asset`` notation:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts
- '@jquery_and_ui'
- '@AppBundle/Resources/public/js/*' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array(
- '@jquery_and_ui',
- '@AppBundle/Resources/public/js/*',
- )
- ) as $url): ?>
-
-
-
-.. _assetic-filters:
-
-Filters
--------
-
-Once they're managed by Assetic, you can apply filters to your assets before
-they are served. This includes filters that compress the output of your assets
-for smaller file sizes (and better frontend optimization). Other filters
-can compile CoffeeScript files to JavaScript and process SASS into CSS.
-In fact, Assetic has a long list of available filters.
-
-Many of the filters do not do the work directly, but use existing third-party
-libraries to do the heavy-lifting. This means that you'll often need to install
-a third-party library to use a filter. The great advantage of using Assetic
-to invoke these libraries (as opposed to using them directly) is that instead
-of having to run them manually after you work on the files, Assetic will
-take care of this for you and remove this step altogether from your development
-and deployment processes.
-
-To use a filter, you first need to specify it in the Assetic configuration.
-Adding a filter here doesn't mean it's being used - it just means that it's
-available to use (you'll use the filter below).
-
-For example to use the UglifyJS JavaScript minifier the following configuration
-should be defined:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- uglifyjs2:
- bin: /usr/local/bin/uglifyjs
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'uglifyjs2' => array(
- 'bin' => '/usr/local/bin/uglifyjs',
- ),
- ),
- ));
-
-Now, to actually *use* the filter on a group of JavaScript files, add it
-into your template:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' filter='uglifyjs2' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array('uglifyjs2')
- ) as $url): ?>
-
-
-
-A more detailed guide about configuring and using Assetic filters as well as
-details of Assetic's debug mode can be found in :doc:`/assetic/uglifyjs`.
-
-Controlling the URL Used
-------------------------
-
-If you wish to, you can control the URLs that Assetic produces. This is
-done from the template and is relative to the public document root:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array(),
- array('output' => 'js/compiled/main.js')
- ) as $url): ?>
-
-
-
-.. note::
-
- Symfony also contains a method for cache *busting*, where the final URL
- generated by Assetic contains a query parameter that can be incremented
- via configuration on each deployment. For more information, see the
- :ref:`reference-framework-assets-version` configuration option.
-
-.. _assetic-dumping:
-
-Dumping Asset Files
--------------------
-
-In the ``dev`` environment, Assetic generates paths to CSS and JavaScript
-files that don't physically exist on your computer. But they render nonetheless
-because an internal Symfony controller opens the files and serves back the
-content (after running any filters).
-
-This kind of dynamic serving of processed assets is great because it means
-that you can immediately see the new state of any asset files you change.
-It's also bad, because it can be quite slow. If you're using a lot of filters,
-it might be downright frustrating.
-
-Fortunately, Assetic provides a way to dump your assets to real files, instead
-of being generated dynamically.
-
-Dumping Asset Files in the ``prod`` Environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In the ``prod`` environment, your JS and CSS files are represented by a single
-tag each. In other words, instead of seeing each JavaScript file you're including
-in your source, you'll likely just see something like this:
-
-.. code-block:: html
-
-
-
-Moreover, that file does **not** actually exist, nor is it dynamically rendered
-by Symfony (as the asset files are in the ``dev`` environment). This is on
-purpose - letting Symfony generate these files dynamically in a production
-environment is just too slow.
-
-.. _assetic-dump-prod:
-
-Instead, each time you use your application in the ``prod`` environment (and therefore,
-each time you deploy), you should run the following command:
-
-.. code-block:: terminal
-
- $ php bin/console assetic:dump --env=prod --no-debug
-
-This will physically generate and write each file that you need (e.g. ``/js/abcd123.js``).
-If you update any of your assets, you'll need to run this again to regenerate
-the file.
-
-Dumping Asset Files in the ``dev`` Environment
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-By default, each asset path generated in the ``dev`` environment is handled
-dynamically by Symfony. This has no disadvantage (you can see your changes
-immediately), except that assets can load noticeably slow. If you feel like
-your assets are loading too slowly, follow this guide.
-
-First, tell Symfony to stop trying to process these files dynamically. Make
-the following change in your ``config_dev.yml`` file:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config_dev.yml
- assetic:
- use_controller: false
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config_dev.php
- $container->loadFromExtension('assetic', array(
- 'use_controller' => false,
- ));
-
-Next, since Symfony is no longer generating these assets for you, you'll
-need to dump them manually. To do so, run the following command:
-
-.. code-block:: terminal
-
- $ php bin/console assetic:dump
-
-This physically writes all of the asset files you need for your ``dev``
-environment. The big disadvantage is that you need to run this each time
-you update an asset. Fortunately, by using the ``assetic:watch`` command,
-assets will be regenerated automatically *as they change*:
-
-.. code-block:: terminal
-
- $ php bin/console assetic:watch
-
-The ``assetic:watch`` command was introduced in AsseticBundle 2.4. In prior
-versions, you had to use the ``--watch`` option of the ``assetic:dump``
-command for the same behavior.
-
-Since running this command in the ``dev`` environment may generate a bunch
-of files, it's usually a good idea to point your generated asset files to
-some isolated directory (e.g. ``/js/compiled``), to keep things organized:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array(),
- array('output' => 'js/compiled/main.js')
- ) as $url): ?>
-
-
-
-.. _`LiipImagineBundle`: https://github.com/liip/LiipImagineBundle
diff --git a/assetic/jpeg_optimize.rst b/assetic/jpeg_optimize.rst
deleted file mode 100644
index 8e6871163d1..00000000000
--- a/assetic/jpeg_optimize.rst
+++ /dev/null
@@ -1,310 +0,0 @@
-.. index::
- single: Assetic; Image optimization
-
-How to Use Assetic for Image Optimization with Twig Functions
-=============================================================
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-Among its many filters, Assetic has four filters which can be used for on-the-fly
-image optimization. This allows you to get the benefits of smaller file sizes
-without having to use an image editor to process each image. The results
-are cached and can be dumped for production so there is no performance hit
-for your end users.
-
-Using Jpegoptim
----------------
-
-`Jpegoptim`_ is a utility for optimizing JPEG files. To use it with Assetic, make
-sure to have it already installed on your system and then, configure its location
-using the ``bin`` option of the ``jpegoptim`` filter:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- jpegoptim:
- bin: path/to/jpegoptim
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'jpegoptim' => array(
- 'bin' => 'path/to/jpegoptim',
- ),
- ),
- ));
-
-It can now be used from a template:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% image '@AppBundle/Resources/public/images/example.jpg'
- filter='jpegoptim' output='/images/example.jpg' %}
-
- {% endimage %}
-
- .. code-block:: html+php
-
- image(
- array('@AppBundle/Resources/public/images/example.jpg'),
- array('jpegoptim')
- ) as $url): ?>
-
-
-
-Removing all EXIF Data
-~~~~~~~~~~~~~~~~~~~~~~
-
-By default, the ``jpegoptim`` filter removes some meta information stored
-in the image. To remove all EXIF data and comments, set the ``strip_all`` option
-to ``true``:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- jpegoptim:
- bin: path/to/jpegoptim
- strip_all: true
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'jpegoptim' => array(
- 'bin' => 'path/to/jpegoptim',
- 'strip_all' => 'true',
- ),
- ),
- ));
-
-Lowering Maximum Quality
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-By default, the ``jpegoptim`` filter doesn't alter the quality level of the JPEG
-image. Use the ``max`` option to configure the maximum quality setting (in a
-scale of ``0`` to ``100``). The reduction in the image file size will of course
-be at the expense of its quality:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- jpegoptim:
- bin: path/to/jpegoptim
- max: 70
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'jpegoptim' => array(
- 'bin' => 'path/to/jpegoptim',
- 'max' => '70',
- ),
- ),
- ));
-
-Shorter Syntax: Twig Function
------------------------------
-
-If you're using Twig, it's possible to achieve all of this with a shorter
-syntax by enabling and using a special Twig function. Start by adding the
-following configuration:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- jpegoptim:
- bin: path/to/jpegoptim
- twig:
- functions:
- jpegoptim: ~
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'jpegoptim' => array(
- 'bin' => 'path/to/jpegoptim',
- ),
- ),
- 'twig' => array(
- 'functions' => array('jpegoptim'),
- ),
- ));
-
-The Twig template can now be changed to the following:
-
-.. code-block:: html+twig
-
-
-
-You can also specify the output directory for images in the Assetic configuration
-file:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- jpegoptim:
- bin: path/to/jpegoptim
- twig:
- functions:
- jpegoptim: { output: images/*.jpg }
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'jpegoptim' => array(
- 'bin' => 'path/to/jpegoptim',
- ),
- ),
- 'twig' => array(
- 'functions' => array(
- 'jpegoptim' => array(
- 'output' => 'images/*.jpg',
- ),
- ),
- ),
- ));
-
-.. tip::
-
- For uploaded images, you can compress and manipulate them using the
- `LiipImagineBundle`_ community bundle.
-
-.. _`Jpegoptim`: http://www.kokkonen.net/tjko/projects.html
-.. _`LiipImagineBundle`: https://github.com/liip/LiipImagineBundle
diff --git a/assetic/php.rst b/assetic/php.rst
deleted file mode 100644
index 8571b99b0c0..00000000000
--- a/assetic/php.rst
+++ /dev/null
@@ -1,214 +0,0 @@
-.. index::
- single: Front-end; Assetic, Bootstrap
-
-Combining, Compiling and Minimizing Web Assets with PHP Libraries
-=================================================================
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-The official Symfony Best Practices recommend to use Assetic to
-:doc:`manage web assets `, unless you are
-comfortable with JavaScript-based front-end tools.
-
-Even if those JavaScript-based solutions are the most suitable ones from a
-technical point of view, using pure PHP alternative libraries can be useful in
-some scenarios:
-
-* If you can't install or use ``npm`` and the other JavaScript solutions;
-* If you prefer to limit the amount of different technologies used in your
- applications;
-* If you want to simplify application deployment.
-
-In this article, you'll learn how to combine and minimize CSS and JavaScript files
-and how to compile Sass files using PHP-only libraries with Assetic.
-
-Installing the Third-Party Compression Libraries
-------------------------------------------------
-
-Assetic includes a lot of ready-to-use filters, but it doesn't include their
-associated libraries. Therefore, before enabling the filters used in this article,
-you must install two libraries. Open a command console, browse to your project
-directory and execute the following commands:
-
-.. code-block:: terminal
-
- $ composer require leafo/scssphp
- $ composer require patchwork/jsqueeze
-
-
-Organizing your Web Asset Files
--------------------------------
-
-This example will include a setup using the Bootstrap CSS framework, jQuery, FontAwesome
-and some regular CSS and JavaScript application files (called ``main.css`` and
-``main.js``). The recommended directory structure for this set-up looks like this:
-
-.. code-block:: text
-
- web/assets/
- ├── css
- │ ├── main.css
- │ └── code-highlight.css
- ├── js
- │ ├── bootstrap.js
- │ ├── jquery.js
- │ └── main.js
- └── scss
- ├── bootstrap
- │ ├── _alerts.scss
- │ ├── ...
- │ ├── _variables.scss
- │ ├── _wells.scss
- │ └── mixins
- │ ├── _alerts.scss
- │ ├── ...
- │ └── _vendor-prefixes.scss
- ├── bootstrap.scss
- ├── font-awesome
- │ ├── _animated.scss
- │ ├── ...
- │ └── _variables.scss
- └── font-awesome.scss
-
-Combining and Minimizing CSS Files and Compiling SCSS Files
------------------------------------------------------------
-
-First, configure a new ``scssphp`` Assetic filter:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- scssphp:
- formatter: 'Leafo\ScssPhp\Formatter\Compressed'
- # ...
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'scssphp' => array(
- 'formatter' => 'Leafo\ScssPhp\Formatter\Compressed',
- ),
- // ...
- ),
- ));
-
-The value of the ``formatter`` option is the fully qualified class name of the
-formatter used by the filter to produce the compiled CSS file. Using the
-compressed formatter will minimize the resulting file, regardless of whether
-the original files are regular CSS files or SCSS files.
-
-Next, update your Twig template to add the ``{% stylesheets %}`` tag defined
-by Assetic:
-
-.. code-block:: html+twig
-
- {# app/Resources/views/base.html.twig #}
-
-
-
-
-
- {% stylesheets filter="scssphp" output="css/app.css"
- "assets/scss/bootstrap.scss"
- "assets/scss/font-awesome.scss"
- "assets/css/*.css"
- %}
-
- {% endstylesheets %}
-
-This simple configuration compiles, combines and minifies the SCSS files into a
-regular CSS file that's put in ``web/css/app.css``. This is the only CSS file
-which will be served to your visitors.
-
-Combining and Minimizing JavaScript Files
------------------------------------------
-
-First, configure a new ``jsqueeze`` Assetic filter as follows:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- jsqueeze: ~
- # ...
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'jsqueeze' => null,
- // ...
- ),
- ));
-
-Next, update the code of your Twig template to add the ``{% javascripts %}`` tag
-defined by Assetic:
-
-.. code-block:: html+twig
-
-
-
- {% javascripts filter="?jsqueeze" output="js/app.js"
- "assets/js/jquery.js"
- "assets/js/bootstrap.js"
- "assets/js/main.js"
- %}
-
- {% endjavascripts %}
-
-
-
-
-This simple configuration combines all the JavaScript files, minimizes the contents
-and saves the output in the ``web/js/app.js`` file, which is the one that is
-served to your visitors.
-
-The leading ``?`` character in the ``jsqueeze`` filter name tells Assetic to only
-apply the filter when *not* in ``debug`` mode. In practice, this means that you'll
-see unminified files while developing and minimized files in the ``prod`` environment.
diff --git a/assetic/uglifyjs.rst b/assetic/uglifyjs.rst
deleted file mode 100644
index 89b39fd7b4a..00000000000
--- a/assetic/uglifyjs.rst
+++ /dev/null
@@ -1,334 +0,0 @@
-.. index::
- single: Assetic; UglifyJS
-
-How to Minify CSS/JS Files (Using UglifyJS and UglifyCSS)
-=========================================================
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-`UglifyJS`_ is a JavaScript parser/compressor/beautifier toolkit. It can be used
-to combine and minify JavaScript assets so that they require less HTTP requests
-and make your site load faster. `UglifyCSS`_ is a CSS compressor/beautifier
-that is very similar to UglifyJS.
-
-In this article, the installation, configuration and usage of UglifyJS is
-shown in detail. UglifyCSS works pretty much the same way and is only
-talked about briefly.
-
-Install UglifyJS
-----------------
-
-UglifyJS is available as a `Node.js`_ module. First, you need to `install Node.js`_
-and then, decide the installation method: global or local.
-
-Global Installation
-~~~~~~~~~~~~~~~~~~~
-
-The global installation method makes all your projects use the very same UglifyJS
-version, which simplifies its maintenance. Open your command console and execute
-the following command (you may need to run it as a root user):
-
-.. code-block:: terminal
-
- $ npm install -g uglify-js
-
-Now you can execute the global ``uglifyjs`` command anywhere on your system:
-
-.. code-block:: terminal
-
- $ uglifyjs --help
-
-Local Installation
-~~~~~~~~~~~~~~~~~~
-
-It's also possible to install UglifyJS inside your project only, which is useful
-when your project requires a specific UglifyJS version. To do this, install it
-without the ``-g`` option and specify the path where to put the module:
-
-.. code-block:: terminal
-
- $ cd /path/to/your/symfony/project
- $ npm install uglify-js --prefix app/Resources
-
-It is recommended that you install UglifyJS in your ``app/Resources`` folder and
-add the ``node_modules`` folder to version control. Alternatively, you can create
-an npm `package.json`_ file and specify your dependencies there.
-
-Now you can execute the ``uglifyjs`` command that lives in the ``node_modules``
-directory:
-
-.. code-block:: terminal
-
- $ "./app/Resources/node_modules/.bin/uglifyjs" --help
-
-Configure the ``uglifyjs2`` Filter
-----------------------------------
-
-Now we need to configure Symfony to use the ``uglifyjs2`` filter when processing
-your JavaScripts:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- uglifyjs2:
- # the path to the uglifyjs executable
- bin: /usr/local/bin/uglifyjs
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'uglifyjs2' => array(
- // the path to the uglifyjs executable
- 'bin' => '/usr/local/bin/uglifyjs',
- ),
- ),
- ));
-
-.. note::
-
- The path where UglifyJS is installed may vary depending on your system.
- To find out where npm stores the ``bin`` folder, execute the following command:
-
- .. code-block:: terminal
-
- $ npm bin -g
-
- It should output a folder on your system, inside which you should find
- the UglifyJS executable.
-
- If you installed UglifyJS locally, you can find the ``bin`` folder inside
- the ``node_modules`` folder. It's called ``.bin`` in this case.
-
-You now have access to the ``uglifyjs2`` filter in your application.
-
-Configure the ``node`` Binary
------------------------------
-
-Assetic tries to find the node binary automatically. If it cannot be found, you
-can configure its location using the ``node`` key:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- # the path to the node executable
- node: /usr/bin/nodejs
- filters:
- uglifyjs2:
- # the path to the uglifyjs executable
- bin: /usr/local/bin/uglifyjs
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'node' => '/usr/bin/nodejs',
- 'uglifyjs2' => array(
- // the path to the uglifyjs executable
- 'bin' => '/usr/local/bin/uglifyjs',
- ),
- ));
-
-Minify your Assets
-------------------
-
-In order to apply UglifyJS on your assets, add the ``filter`` option in the
-asset tags of your templates to tell Assetic to use the ``uglifyjs2`` filter:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' filter='uglifyjs2' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array('uglifyj2s')
- ) as $url): ?>
-
-
-
-.. note::
-
- The above example assumes that you have a bundle called AppBundle and your
- JavaScript files are in the ``Resources/public/js`` directory under your
- bundle. However you can include your JavaScript files no matter where they are.
-
-With the addition of the ``uglifyjs2`` filter to the asset tags above, you
-should now see minified JavaScripts coming over the wire much faster.
-
-Disable Minification in Debug Mode
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Minified JavaScripts are very difficult to read, let alone debug. Because of
-this, Assetic lets you disable a certain filter when your application is in
-debug (e.g. ``app_dev.php``) mode. You can do this by prefixing the filter name
-in your template with a question mark: ``?``. This tells Assetic to only
-apply this filter when debug mode is off (e.g. ``app.php``):
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' filter='?uglifyjs2' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array('?uglifyjs2')
- ) as $url): ?>
-
-
-
-To try this out, switch to your ``prod`` environment (``app.php``). But before
-you do, don't forget to :ref:`clear your cache `
-and :ref:`dump your assetic assets `.
-
-.. tip::
-
- Instead of adding the filters to the asset tags, you can also configure which
- filters to apply for each file in your application configuration file.
- See :ref:`assetic-apply-to` for more details.
-
-Install, Configure and Use UglifyCSS
-------------------------------------
-
-The usage of UglifyCSS works the same way as UglifyJS. First, make sure
-the node package is installed:
-
-.. code-block:: terminal
-
- # global installation
- $ npm install -g uglifycss
-
- # local installation
- $ cd /path/to/your/symfony/project
- $ npm install uglifycss --prefix app/Resources
-
-Next, add the configuration for this filter:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- filters:
- uglifycss:
- bin: /usr/local/bin/uglifycss
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- 'filters' => array(
- 'uglifycss' => array(
- 'bin' => '/usr/local/bin/uglifycss',
- ),
- ),
- ));
-
-To use the filter for your CSS files, add the filter to the Assetic ``stylesheets``
-helper:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% stylesheets 'bundles/App/css/*' filter='uglifycss' filter='cssrewrite' %}
-
- {% endstylesheets %}
-
- .. code-block:: html+php
-
- stylesheets(
- array('bundles/App/css/*'),
- array('uglifycss'),
- array('cssrewrite')
- ) as $url): ?>
-
-
-
-Just like with the ``uglifyjs2`` filter, if you prefix the filter name with
-``?`` (i.e. ``?uglifycss``), the minification will only happen when you're
-not in debug mode.
-
-.. _`UglifyJS`: https://github.com/mishoo/UglifyJS
-.. _`UglifyCSS`: https://github.com/fmarcia/UglifyCSS
-.. _`Node.js`: https://nodejs.org/
-.. _`install Node.js`: https://nodejs.org/
-.. _`package.json`: http://browsenpm.org/package.json
diff --git a/assetic/yuicompressor.rst b/assetic/yuicompressor.rst
deleted file mode 100644
index 92a3ed0348d..00000000000
--- a/assetic/yuicompressor.rst
+++ /dev/null
@@ -1,180 +0,0 @@
-.. index::
- single: Assetic; YUI Compressor
-
-How to Minify JavaScripts and Stylesheets with YUI Compressor
-=============================================================
-
-.. caution::
-
- The YUI Compressor is `no longer maintained by Yahoo`_. That's why you are
- **strongly advised to avoid using YUI utilities** unless strictly necessary.
- Read :doc:`/assetic/uglifyjs` for a modern and up-to-date alternative.
-
-.. include:: /assetic/_standard_edition_warning.rst.inc
-
-Yahoo! provides an excellent utility for minifying JavaScripts and stylesheets
-so they travel over the wire faster, the `YUI Compressor`_. Thanks to Assetic,
-you can take advantage of this tool very easily.
-
-Download the YUI Compressor JAR
--------------------------------
-
-The YUI Compressor is written in Java and distributed as a JAR. `Download the JAR`_
-from the Yahoo! website and save it to ``app/Resources/java/yuicompressor.jar``.
-
-Configure the YUI Filters
--------------------------
-
-Now you need to configure two Assetic filters in your application, one for
-minifying JavaScripts with the YUI Compressor and one for minifying
-stylesheets:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- assetic:
- # java: '/usr/bin/java'
- filters:
- yui_css:
- jar: '%kernel.project_dir%/app/Resources/java/yuicompressor.jar'
- yui_js:
- jar: '%kernel.project_dir%/app/Resources/java/yuicompressor.jar'
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->loadFromExtension('assetic', array(
- // 'java' => '/usr/bin/java',
- 'filters' => array(
- 'yui_css' => array(
- 'jar' => '%kernel.project_dir%/app/Resources/java/yuicompressor.jar',
- ),
- 'yui_js' => array(
- 'jar' => '%kernel.project_dir%/app/Resources/java/yuicompressor.jar',
- ),
- ),
- ));
-
-.. note::
-
- Windows users need to remember to update config to proper Java location.
- In Windows7 x64 bit by default it's ``C:\Program Files (x86)\Java\jre6\bin\java.exe``.
-
-You now have access to two new Assetic filters in your application:
-``yui_css`` and ``yui_js``. These will use the YUI Compressor to minify
-stylesheets and JavaScripts, respectively.
-
-Minify your Assets
-------------------
-
-You have YUI Compressor configured now, but nothing is going to happen until
-you apply one of these filters to an asset. Since your assets are a part of
-the view layer, this work is done in your templates:
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' filter='yui_js' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array('yui_js')
- ) as $url): ?>
-
-
-
-.. note::
-
- The above example assumes that you have a bundle called AppBundle and your
- JavaScript files are in the ``Resources/public/js`` directory under your
- bundle. This isn't important however - you can include your JavaScript
- files no matter where they are.
-
-With the addition of the ``yui_js`` filter to the asset tags above, you should
-now see minified JavaScripts coming over the wire much faster. The same process
-can be repeated to minify your stylesheets.
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% stylesheets '@AppBundle/Resources/public/css/*' filter='yui_css' %}
-
- {% endstylesheets %}
-
- .. code-block:: html+php
-
- stylesheets(
- array('@AppBundle/Resources/public/css/*'),
- array('yui_css')
- ) as $url): ?>
-
-
-
-Disable Minification in Debug Mode
-----------------------------------
-
-Minified JavaScripts and stylesheets are very difficult to read, let alone
-debug. Because of this, Assetic lets you disable a certain filter when your
-application is in debug mode. You can do this by prefixing the filter name
-in your template with a question mark: ``?``. This tells Assetic to only
-apply this filter when debug mode is off.
-
-.. configuration-block::
-
- .. code-block:: html+twig
-
- {% javascripts '@AppBundle/Resources/public/js/*' filter='?yui_js' %}
-
- {% endjavascripts %}
-
- .. code-block:: html+php
-
- javascripts(
- array('@AppBundle/Resources/public/js/*'),
- array('?yui_js')
- ) as $url): ?>
-
-
-
-.. tip::
-
- Instead of adding the filter to the asset tags, you can also globally
- enable it by adding the ``apply_to`` attribute to the filter configuration, for
- example in the ``yui_js`` filter ``apply_to: "\.js$"``. To only have the filter
- applied in production, add this to the ``config_prod`` file rather than the
- common config file. For details on applying filters by file extension,
- see :ref:`assetic-apply-to`.
-
-.. _`YUI Compressor`: http://yui.github.io/yuicompressor/
-.. _`Download the JAR`: https://github.com/yui/yuicompressor/releases
-.. _`no longer maintained by Yahoo`: http://yuiblog.com/blog/2013/01/24/yui-compressor-has-a-new-owner/
diff --git a/best_practices.rst b/best_practices.rst
new file mode 100644
index 00000000000..b887d4d289a
--- /dev/null
+++ b/best_practices.rst
@@ -0,0 +1,456 @@
+The Symfony Framework Best Practices
+====================================
+
+This article describes the **best practices for developing web applications with
+Symfony** that fit the philosophy envisioned by the original Symfony creators.
+
+If you don't agree with some of these recommendations, they might be a good
+**starting point** that you can then **extend and fit to your specific needs**.
+You can even ignore them completely and continue using your own best practices
+and methodologies. Symfony is flexible enough to adapt to your needs.
+
+This article assumes that you already have experience developing Symfony
+applications. If you don't, read first the :doc:`Getting Started `
+section of the documentation.
+
+.. tip::
+
+ Symfony provides a sample application called `Symfony Demo`_ that follows
+ all these best practices, so you can experience them in practice.
+
+Creating the Project
+--------------------
+
+Use the Symfony Binary to Create Symfony Applications
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Symfony binary is an executable command created in your machine when you
+`download Symfony`_. It provides multiple utilities, including the simplest way
+to create new Symfony applications:
+
+.. code-block:: terminal
+
+ $ symfony new my_project_directory
+
+Under the hood, this Symfony binary command executes the needed `Composer`_
+command to :ref:`create a new Symfony application `
+based on the current stable version.
+
+Use the Default Directory Structure
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unless your project follows a development practice that imposes a certain
+directory structure, follow the default Symfony directory structure. It's flat,
+self-explanatory and not coupled to Symfony:
+
+.. code-block:: text
+
+ your_project/
+ ├─ assets/
+ ├─ bin/
+ │ └─ console
+ ├─ config/
+ │ ├─ packages/
+ │ ├─ routes/
+ │ └─ services.yaml
+ ├─ migrations/
+ ├─ public/
+ │ ├─ build/
+ │ └─ index.php
+ ├─ src/
+ │ ├─ Kernel.php
+ │ ├─ Command/
+ │ ├─ Controller/
+ │ ├─ DataFixtures/
+ │ ├─ Entity/
+ │ ├─ EventSubscriber/
+ │ ├─ Form/
+ │ ├─ Repository/
+ │ ├─ Security/
+ │ └─ Twig/
+ ├─ templates/
+ ├─ tests/
+ ├─ translations/
+ ├─ var/
+ │ ├─ cache/
+ │ └─ log/
+ └─ vendor/
+
+Configuration
+-------------
+
+Use Environment Variables for Infrastructure Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The values of these options change from one machine to another (e.g. from your
+development machine to the production server), but they don't modify the
+application behavior.
+
+:ref:`Use env vars in your project ` to define these options
+and create multiple ``.env`` files to :ref:`configure env vars per environment `.
+
+.. _use-secret-for-sensitive-information:
+
+Use Secrets for Sensitive Information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When your application has sensitive configuration, like an API key, you should
+store those securely via :doc:`Symfony’s secrets management system `.
+
+Use Parameters for Application Configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These are the options used to modify the application behavior, such as the sender
+of email notifications, or the enabled `feature toggles`_. Their value doesn't
+change per machine, so don't define them as environment variables.
+
+Define these options as :ref:`parameters ` in the
+``config/services.yaml`` file. You can override these options per
+:ref:`environment ` in the ``config/services_dev.yaml``
+and ``config/services_prod.yaml`` files.
+
+Unless the application configuration is reused multiple times and needs
+rigid validation, do *not* use the :doc:`Config component `
+to define the options.
+
+Use Short and Prefixed Parameter Names
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Consider using ``app.`` as the prefix of your :ref:`parameters `
+to avoid collisions with Symfony and third-party bundles/libraries parameters.
+Then, use just one or two words to describe the purpose of the parameter:
+
+.. code-block:: yaml
+
+ # config/services.yaml
+ parameters:
+ # don't do this: 'dir' is too generic, and it doesn't convey any meaning
+ app.dir: '...'
+ # do this: short but easy to understand names
+ app.contents_dir: '...'
+ # it's OK to use dots, underscores, dashes or nothing, but always
+ # be consistent and use the same format for all the parameters
+ app.dir.contents: '...'
+ app.contents-dir: '...'
+
+Use Constants to Define Options that Rarely Change
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configuration options like the number of items to display in some listing rarely
+change. Instead of defining them as :ref:`configuration parameters `,
+define them as PHP constants in the related classes. Example::
+
+ // src/Entity/Post.php
+ namespace App\Entity;
+
+ class Post
+ {
+ public const NUMBER_OF_ITEMS = 10;
+
+ // ...
+ }
+
+The main advantage of constants is that you can use them everywhere, including
+Twig templates and Doctrine entities, whereas parameters are only available
+from places with access to the :doc:`service container `.
+
+The only notable disadvantage of using constants for this kind of configuration
+values is that it's complicated to redefine their values in your tests.
+
+Business Logic
+--------------
+
+.. _best-practice-no-application-bundles:
+
+Don't Create any Bundle to Organize your Application Logic
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When Symfony 2.0 was released, applications used :doc:`bundles ` to
+divide their code into logical features: UserBundle, ProductBundle,
+InvoiceBundle, etc. However, a bundle is meant to be something that can be
+reused as a stand-alone piece of software.
+
+If you need to reuse some feature in your projects, create a bundle for it (in a
+private repository, do not make it publicly available). For the rest of your
+application code, use PHP namespaces to organize code instead of bundles.
+
+Use Autowiring to Automate the Configuration of Application Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:doc:`Service autowiring ` is a feature that
+reads the type-hints on your constructor (or other methods) and automatically
+passes the correct services to each method, making it unnecessary to configure
+services explicitly and simplifying the application maintenance.
+
+Use it in combination with :ref:`service autoconfiguration `
+to also add :doc:`service tags ` to the services
+needing them, such as Twig extensions, event subscribers, etc.
+
+Services Should be Private Whenever Possible
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:ref:`Make services private ` to prevent you from accessing
+those services via ``$container->get()``. Instead, you will need to use proper
+dependency injection.
+
+Use the YAML Format to Configure your own Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you use the :ref:`default services.yaml configuration `,
+most services will be configured automatically. However, in some edge cases
+you'll need to configure services (or parts of them) manually.
+
+YAML is the format recommended configuring services because it's friendly to
+newcomers and concise, but Symfony also supports XML and PHP configuration.
+
+Use Attributes to Define the Doctrine Entity Mapping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Doctrine entities are plain PHP objects that you store in some "database".
+Doctrine only knows about your entities through the mapping metadata configured
+for your model classes.
+
+Doctrine supports several metadata formats, but it's recommended to use PHP
+attributes because they are by far the most convenient and agile way of setting
+up and looking for mapping information.
+
+Controllers
+-----------
+
+Make your Controller Extend the ``AbstractController`` Base Controller
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Symfony provides a :ref:`base controller `
+which includes shortcuts for the most common needs such as rendering templates
+or checking security permissions.
+
+Extending your controllers from this base controller couples your application
+to Symfony. Coupling is generally wrong, but it may be OK in this case because
+controllers shouldn't contain any business logic. Controllers should contain
+nothing more than a few lines of *glue-code*, so you are not coupling the
+important parts of your application.
+
+.. _best-practice-controller-annotations:
+.. _best-practice-controller-attributes:
+
+Use Attributes to Configure Routing, Caching, and Security
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Using attributes for routing, caching, and security simplifies
+configuration. You don't need to browse several files created with different
+formats (YAML, XML, PHP): all the configuration is just where you require it,
+and it only uses one format.
+
+Use Dependency Injection to Get Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you extend the base ``AbstractController``, you can only get access to the most
+common services (e.g ``twig``, ``router``, ``doctrine``, etc.), directly from the
+container via ``$this->container->get()``.
+Instead, you must use dependency injection to fetch services by
+:ref:`type-hinting action method arguments ` or
+constructor arguments.
+
+Use Entity Value Resolvers If They Are Convenient
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you're using :doc:`Doctrine `, then you can *optionally* use
+the :ref:`EntityValueResolver ` to
+automatically query for an entity and pass it as an argument to your
+controller. It will also show a 404 page if no entity can be found.
+
+If the logic to get an entity from a route variable is more complex, instead of
+configuring the EntityValueResolver, it's better to make the Doctrine query
+inside the controller (e.g. by calling to a :doc:`Doctrine repository method `).
+
+Templates
+---------
+
+Use Snake Case for Template Names and Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use lowercase snake_case for template names, directories, and variables (e.g.
+``user_profile`` instead of ``userProfile`` and ``product/edit_form.html.twig``
+instead of ``Product/EditForm.html.twig``).
+
+Prefix Template Fragments with an Underscore
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Template fragments, also called *"partial templates"*, allow to
+:ref:`reuse template contents `. Prefix their names
+with an underscore to better differentiate them from complete templates (e.g.
+``_user_metadata.html.twig`` or ``_caution_message.html.twig``).
+
+Forms
+-----
+
+Define your Forms as PHP Classes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Creating :ref:`forms in classes ` allows reusing
+them in different parts of the application. Besides, not creating forms in
+controllers simplifies the code and maintenance of the controllers.
+
+Add Form Buttons in Templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Form classes should be agnostic to where they will be used. For example, the
+button of a form used to both create and edit items should change from "Add new"
+to "Save changes" depending on where it's used.
+
+Instead of adding buttons in form classes or the controllers, it's recommended
+to add buttons in the templates. This also improves the separation of concerns
+because the button styling (CSS class and other attributes) is defined in the
+template instead of in a PHP class.
+
+However, if you create a :doc:`form with multiple submit buttons `
+you should define them in the controller instead of the template. Otherwise, you
+won't be able to check which button was clicked when handling the form in the controller.
+
+Define Validation Constraints on the Underlying Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Attaching :doc:`validation constraints ` to form fields
+instead of to the mapped object prevents the validation from being reused in
+other forms or other places where the object is used.
+
+.. _best-practice-handle-form:
+
+Use a Single Action to Render and Process the Form
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:ref:`Rendering forms ` and :ref:`processing forms `
+are two of the main tasks when handling forms. Both are too similar (most of the
+time, almost identical), so it's much simpler to let a single controller action
+handle both.
+
+.. _best-practice-internationalization:
+
+Internationalization
+--------------------
+
+Use the XLIFF Format for Your Translation Files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Of all the translation formats supported by Symfony (PHP, Qt, ``.po``, ``.mo``,
+JSON, CSV, INI, etc.), ``XLIFF`` and ``gettext`` have the best support in the tools used
+by professional translators. And since it's based on XML, you can validate ``XLIFF``
+file contents as you write them.
+
+Symfony also supports notes in XLIFF files, making them more user-friendly for
+translators. At the end, good translations are all about context, and these
+XLIFF notes allow you to define that context.
+
+Use Keys for Translations Instead of Content Strings
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Using keys simplifies the management of the translation files because you can
+change the original contents in templates, controllers, and services without
+having to update all the translation files.
+
+Keys should always describe their *purpose* and *not* their location. For
+example, if a form has a field with the label "Username", then a nice key
+would be ``label.username``, *not* ``edit_form.label.username``.
+
+Security
+--------
+
+Define a Single Firewall
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unless you have two legitimately different authentication systems and users
+(e.g. form login for the main site and a token system for your API only), it's
+recommended to have only one firewall to keep things simple.
+
+Use the ``auto`` Password Hasher
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :ref:`auto password hasher ` automatically
+selects the best possible encoder/hasher depending on your PHP installation.
+Currently, the default auto hasher is ``bcrypt``.
+
+Use Voters to Implement Fine-grained Security Restrictions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your security logic is complex, you should create custom
+:doc:`security voters ` instead of defining long expressions
+inside the ``#[Security]`` attribute.
+
+Web Assets
+----------
+
+.. _use-webpack-encore-to-process-web-assets:
+
+Use AssetMapper to Manage Web Assets
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Web assets are the CSS, JavaScript, and image files that make the frontend of
+your site look and work great. :doc:`AssetMapper ` lets
+you write modern JavaScript and CSS without the complexity of using a bundler
+such as `Webpack`_ (directly or via :doc:`Webpack Encore `).
+
+Tests
+-----
+
+Smoke Test your URLs
+~~~~~~~~~~~~~~~~~~~~
+
+In software engineering, `smoke testing`_ consists of *"preliminary testing to
+reveal simple failures severe enough to reject a prospective software release"*.
+Using `PHPUnit data providers`_ you can define a functional test that
+checks that all application URLs load successfully::
+
+ // tests/ApplicationAvailabilityFunctionalTest.php
+ namespace App\Tests;
+
+ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+ class ApplicationAvailabilityFunctionalTest extends WebTestCase
+ {
+ /**
+ * @dataProvider urlProvider
+ */
+ public function testPageIsSuccessful($url): void
+ {
+ $client = self::createClient();
+ $client->request('GET', $url);
+
+ $this->assertResponseIsSuccessful();
+ }
+
+ public function urlProvider(): \Generator
+ {
+ yield ['/'];
+ yield ['/posts'];
+ yield ['/post/fixture-post-1'];
+ yield ['/blog/category/fixture-category'];
+ yield ['/archives'];
+ // ...
+ }
+ }
+
+Add this test while creating your application because it requires little effort
+and checks that none of your pages returns an error. Later, you'll add more
+specific tests for each page.
+
+.. _hardcode-urls-in-a-functional-test:
+
+Hard-code URLs in a Functional Test
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In Symfony applications, it's recommended to :ref:`generate URLs `
+using routes to automatically update all links when a URL changes. However, if a
+public URL changes, users won't be able to browse it unless you set up a
+redirection to the new URL.
+
+That's why it's recommended to use raw URLs in tests instead of generating them
+from routes. Whenever a route changes, tests will fail, and you'll know that
+you must set up a redirection.
+
+.. _`Symfony Demo`: https://github.com/symfony/demo
+.. _`download Symfony`: https://symfony.com/download
+.. _`Composer`: https://getcomposer.org/
+.. _`feature toggles`: https://en.wikipedia.org/wiki/Feature_toggle
+.. _`smoke testing`: https://en.wikipedia.org/wiki/Smoke_testing_(software)
+.. _`Webpack`: https://webpack.js.org/
+.. _`PHPUnit data providers`: https://docs.phpunit.de/en/9.6/writing-tests-for-phpunit.html#data-providers
diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst
deleted file mode 100644
index cb3f6e5413d..00000000000
--- a/best_practices/business-logic.rst
+++ /dev/null
@@ -1,367 +0,0 @@
-Organizing Your Business Logic
-==============================
-
-In computer software, **business logic** or domain logic is "the part of the
-program that encodes the real-world business rules that determine how data can
-be created, displayed, stored, and changed" (read `full definition`_).
-
-In Symfony applications, business logic is all the custom code you write for
-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.
-Inside here, you can create whatever directories you want to organize things:
-
-.. code-block:: text
-
- symfony-project/
- ├─ app/
- ├─ src/
- │ └─ AppBundle/
- │ └─ Utils/
- │ └─ MyClass.php
- ├─ tests/
- ├─ var/
- ├─ vendor/
- └─ web/
-
-Storing Classes Outside of the Bundle?
---------------------------------------
-
-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:
-
-.. code-block:: text
-
- symfony-project/
- ├─ app/
- ├─ src/
- │ ├─ Acme/
- │ │ └─ Utils/
- │ │ └─ MyClass.php
- │ └─ AppBundle/
- ├─ tests/
- ├─ var/
- ├─ vendor/
- └─ web/
-
-.. tip::
-
- 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.
-
-Services: Naming and Format
----------------------------
-
-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.
-
-Let's create a new ``Slugger`` class inside ``src/AppBundle/Utils/`` and
-add the following ``slugify()`` method:
-
-.. code-block:: php
-
- // src/AppBundle/Utils/Slugger.php
- namespace AppBundle\Utils;
-
- class Slugger
- {
- public function slugify($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.
-
-.. best-practice::
-
- The id of your application's services should be equal to their 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``:
-
-.. code-block:: php
-
- use AppBundle\Utils\Slugger;
-
- public function createAction(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 = $this->get('app.slugger')->slugify($post->getTitle());
- $post->setSlug($slug);
-
- // ...
- }
- }
-
-Services can also be :ref:`public or private `. If you use the
-:ref:`default services.yml configuration `,
-all services are private by default.
-
-.. best-practice::
-
- Services should be ``private`` whenever possible. This will prevent you from
- accessing that service via ``$container->get()``. Instead, you will need to use
- dependency injection.
-
-Service Format: YAML
---------------------
-
-In the previous section, YAML was used to define the service.
-
-.. best-practice::
-
- Use the YAML format to define your own services.
-
-This is controversial, and in our experience, YAML and XML usage is evenly
-distributed among developers, with a slight preference towards YAML.
-Both formats have the same performance, so this is ultimately a matter of
-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
--------------------------
-
-Symfony is an HTTP framework that only cares about generating an HTTP response
-for each HTTP request. That's why Symfony doesn't provide a way to talk to
-a persistence layer (e.g. database, external API). You can choose whatever
-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.
-
-The three entities defined by our sample blog application are a good example:
-
-.. code-block:: text
-
- 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/``.
-
-Doctrine Mapping Information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Doctrine entities are plain PHP objects that you store in some "database".
-Doctrine only knows about your entities through the mapping metadata configured
-for your model classes. Doctrine supports four metadata formats: YAML, XML,
-PHP and annotations.
-
-.. best-practice::
-
- Use annotations to define the mapping information of the Doctrine entities.
-
-Annotations are by far the most convenient and agile way of setting up and
-looking for mapping information:
-
-.. code-block:: php
-
- namespace AppBundle\Entity;
-
- use Doctrine\ORM\Mapping as ORM;
- use Doctrine\Common\Collections\ArrayCollection;
-
- /**
- * @ORM\Entity
- */
- class Post
- {
- const NUM_ITEMS = 10;
-
- /**
- * @ORM\Id
- * @ORM\GeneratedValue
- * @ORM\Column(type="integer")
- */
- private $id;
-
- /**
- * @ORM\Column(type="string")
- */
- private $title;
-
- /**
- * @ORM\Column(type="string")
- */
- private $slug;
-
- /**
- * @ORM\Column(type="text")
- */
- private $content;
-
- /**
- * @ORM\Column(type="string")
- */
- private $authorEmail;
-
- /**
- * @ORM\Column(type="datetime")
- */
- private $publishedAt;
-
- /**
- * @ORM\OneToMany(
- * targetEntity="Comment",
- * mappedBy="post",
- * orphanRemoval=true
- * )
- * @ORM\OrderBy({"publishedAt" = "ASC"})
- */
- private $comments;
-
- public function __construct()
- {
- $this->publishedAt = new \DateTime();
- $this->comments = new ArrayCollection();
- }
-
- // getters and setters ...
- }
-
-All formats have the same performance, so this is once again ultimately a
-matter of taste.
-
-Data Fixtures
-~~~~~~~~~~~~~
-
-As fixtures support is not enabled by default in Symfony, you should execute
-the following command to install the Doctrine fixtures bundle:
-
-.. code-block:: terminal
-
- $ composer require "doctrine/doctrine-fixtures-bundle"
-
-Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and
-``test`` environments:
-
-.. code-block:: php
-
- 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;
- }
-
- // ...
- }
-
-We recommend creating just *one* `fixture class`_ for simplicity, though
-you're welcome to have more if that class gets quite large.
-
-Assuming you have at least one fixtures class and that the database access
-is configured properly, you can load your fixtures by executing the following
-command:
-
-.. code-block:: terminal
-
- $ php bin/console doctrine:fixtures:load
-
- Careful, database will be purged. Do you want to continue Y/N ? Y
- > purging database
- > loading AppBundle\DataFixtures\ORM\LoadFixtures
-
-Coding Standards
-----------------
-
-The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that
-were defined by the PHP community. You can learn more about
-:doc:`the Symfony Coding standards ` and even
-use the `PHP-CS-Fixer`_, which is a command-line utility that can fix the
-coding standards of an entire codebase in a matter of seconds.
-
-.. _`full definition`: https://en.wikipedia.org/wiki/Business_logic
-.. _`Doctrine project`: http://www.doctrine-project.org/
-.. _`fixture class`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures
-.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/
-.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/
-.. _`PHP-CS-Fixer`: https://github.com/FriendsOfPHP/PHP-CS-Fixer
diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst
deleted file mode 100644
index fae2144091f..00000000000
--- a/best_practices/configuration.rst
+++ /dev/null
@@ -1,219 +0,0 @@
-Configuration
-=============
-
-Configuration usually involves different application parts (such as infrastructure
-and security credentials) and different environments (development, production).
-That's why Symfony recommends that you split the application configuration into
-three parts.
-
-.. _config-parameters.yml:
-
-Infrastructure-Related Configuration
-------------------------------------
-
-.. best-practice::
-
- Define the infrastructure-related configuration options in the
- ``app/config/parameters.yml`` file.
-
-The default ``parameters.yml`` file follows this recommendation and defines the
-options related to the database and mail server infrastructure:
-
-.. code-block:: yaml
-
- # 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: ~
-
- mailer_transport: smtp
- mailer_host: 127.0.0.1
- mailer_user: ~
- mailer_password: ~
-
- # ...
-
-These options aren't defined inside the ``app/config/config.yml`` 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.
-
-.. _best-practices-canonical-parameters:
-
-Canonical Parameters
-~~~~~~~~~~~~~~~~~~~~
-
-.. best-practice::
-
- Define all your application's parameters in the
- ``app/config/parameters.yml.dist`` file.
-
-Symfony includes a configuration file called ``parameters.yml.dist``, which
-stores the canonical list of configuration parameters 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.
-
-Application-Related Configuration
----------------------------------
-
-.. best-practice::
-
- Define the application behavior related configuration options in the
- ``app/config/config.yml`` 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 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``
-files so that you can override specific values for each environment.
-
-Constants vs Configuration Options
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-One of the most common errors when defining application configuration is to
-create new options for values that never change, such as the number of items for
-paginated results.
-
-.. best-practice::
-
- Use constants to define configuration options that rarely change.
-
-The traditional approach for defining configuration options has caused many
-Symfony apps to include an option like the following, which would be used
-to control the number of posts to display on the blog homepage:
-
-.. code-block:: yaml
-
- # app/config/config.yml
- parameters:
- homepage.num_items: 10
-
-If you've done something like this in the past, it's likely that you've in fact
-*never* actually needed to change that value. Creating a configuration
-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 ``NUM_ITEMS`` constant in the ``Post`` entity::
-
- // src/AppBundle/Entity/Post.php
- namespace AppBundle\Entity;
-
- class Post
- {
- const NUM_ITEMS = 10;
-
- // ...
- }
-
-The main advantage of defining constants is that you can use their values
-everywhere in your application. When using parameters, they are only available
-from places with access to the Symfony container.
-
-Constants can be used for example in your Twig templates thanks to the
-`constant() function`_:
-
-.. code-block:: html+twig
-
-
- Displaying the {{ constant('NUM_ITEMS', post) }} most recent results.
-
-
-And Doctrine entities and repositories can now easily access these values,
-whereas they cannot access the container parameters:
-
-.. code-block:: php
-
- namespace AppBundle\Repository;
-
- use Doctrine\ORM\EntityRepository;
- use AppBundle\Entity\Post;
-
- class PostRepository extends EntityRepository
- {
- public function findLatest($limit = Post::NUM_ITEMS)
- {
- // ...
- }
- }
-
-The only notable disadvantage of using constants for this kind of configuration
-values is that you cannot redefine them easily in your tests.
-
-Parameter Naming
-----------------
-
-.. best-practice::
-
- The name of your configuration parameters should be as short as possible and
- should include a common prefix for the entire application.
-
-Using ``app.`` as the prefix of your parameters is a common practice to avoid
-collisions with Symfony and third-party bundles/libraries parameters. Then, use
-just one or two words to describe the purpose of the parameter:
-
-.. code-block:: yaml
-
- # app/config/config.yml
- parameters:
- # don't do this: 'dir' is too generic and it doesn't convey any meaning
- app.dir: '...'
- # do this: short but easy to understand names
- app.contents_dir: '...'
- # it's OK to use dots, underscores, dashes or nothing, but always
- # be consistent and use the same format for all the parameters
- 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 `.
-
-.. _`feature toggles`: https://en.wikipedia.org/wiki/Feature_toggle
-.. _`constant() function`: http://twig.sensiolabs.org/doc/functions/constant.html
diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst
deleted file mode 100644
index 89f6a497493..00000000000
--- a/best_practices/controllers.rst
+++ /dev/null
@@ -1,229 +0,0 @@
-Controllers
-===========
-
-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.
-
-.. best-practice::
-
- Make your controller extend the FrameworkBundle base controller 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.
-
-And since your controllers should be thin and contain nothing more than a
-few lines of *glue-code*, spending hours trying to decouple them from your
-framework doesn't benefit you in the long run. The amount of time *wasted*
-isn't worth the benefit.
-
-In addition, using annotations for routing, caching and security simplifies
-configuration. You don't need to browse tens of files created with different
-formats (YAML, XML, PHP): all the configuration is just where you need it
-and it only uses one format.
-
-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.
-
-Routing Configuration
----------------------
-
-To load routes defined as annotations in your controllers, add the following
-configuration to the main routing configuration file:
-
-.. code-block:: yaml
-
- # app/config/routing.yml
- app:
- resource: '@AppBundle/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:
-
-.. code-block:: text
-
- /
- ├─ ...
- └─ src/
- └─ AppBundle/
- ├─ ...
- └─ Controller/
- ├─ DefaultController.php
- ├─ ...
- ├─ Api/
- │ ├─ ...
- │ └─ ...
- └─ Backend/
- ├─ ...
- └─ ...
-
-Template Configuration
-----------------------
-
-.. best-practice::
-
- Don't use the ``@Template`` annotation to configure the template used by
- the controller.
-
-The ``@Template`` annotation is useful, but also involves some magic. We
-don't think its benefit is worth the magic, and so recommend against using
-it.
-
-Most of the time, ``@Template`` is used without any parameters, which makes
-it more difficult to know which template is being rendered. It also makes
-it less obvious to beginners that a controller should always return a Response
-object (unless you're using a view layer).
-
-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:
-
-.. code-block:: php
-
- namespace AppBundle\Controller;
-
- use Symfony\Bundle\FrameworkBundle\Controller\Controller;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
- use Doctrine\ORM\EntityManagerInterface;
-
- class DefaultController extends Controller
- {
- /**
- * @Route("/", name="homepage")
- */
- public function indexAction(EntityManagerInterface $em)
- {
- $posts = $em->getRepository('AppBundle:Post')
- ->findLatest();
-
- return $this->render('default/index.html.twig', array(
- '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
-:ref:`type-hinting action method arguments `:
-
-.. best-practice::
-
- Don't use ``$this->get()`` or ``$this->container->get()`` to fetch services
- from the container. Instead, use dependency injection.
-
-By not fetching services directly from the container, you can make your services
-*private*, which has :ref:`several advantages `.
-
-.. _best-practices-paramconverter:
-
-Using the ParamConverter
-------------------------
-
-If you're using Doctrine, then you can *optionally* use the `ParamConverter`_
-to automatically query for an entity and pass it as an argument to your controller.
-
-.. best-practice::
-
- Use the ParamConverter trick to automatically query for Doctrine entities
- when it's simple and convenient.
-
-For example:
-
-.. code-block:: php
-
- use AppBundle\Entity\Post;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
-
- /**
- * @Route("/{id}", name="admin_post_show")
- */
- public function showAction(Post $post)
- {
- $deleteForm = $this->createDeleteForm($post);
-
- return $this->render('admin/post/show.html.twig', array(
- '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.
-
-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``:
-
-.. code-block:: php
-
- /**
- * @Route("/comment/{postSlug}/new", name = "comment_new")
- */
- public function newAction(Request $request, $postSlug)
- {
- $post = $this->getDoctrine()
- ->getRepository('AppBundle:Post')
- ->findOneBy(array('slug' => $postSlug));
-
- if (!$post) {
- throw $this->createNotFoundException();
- }
-
- // ...
- }
-
-You can also use the ``@ParamConverter`` configuration, which is infinitely
-flexible:
-
-.. code-block:: php
-
- use AppBundle\Entity\Post;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
- use Symfony\Component\HttpFoundation\Request;
-
- /**
- * @Route("/comment/{postSlug}/new", name = "comment_new")
- * @ParamConverter("post", options={"mapping": {"postSlug": "slug"}})
- */
- public function newAction(Request $request, Post $post)
- {
- // ...
- }
-
-The point is this: the ParamConverter shortcut is great for simple situations.
-But you shouldn't forget that querying for entities directly is still very
-easy.
-
-Pre and Post Hooks
-------------------
-
-If you need to execute some code before or after the execution of your controllers,
-you can use the EventDispatcher component to
-:doc:`set up before and after filters `.
-
-.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst
deleted file mode 100644
index 7aa8236a2d5..00000000000
--- a/best_practices/creating-the-project.rst
+++ /dev/null
@@ -1,174 +0,0 @@
-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 the Symfony Installer to create new Symfony-based projects.
-
-Read the :doc:`/setup` article learn how to install and use the Symfony
-Installer.
-
-.. _linux-and-mac-os-x-systems:
-.. _windows-systems:
-
-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:
-
-.. 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.
-
-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.
-
-.. 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.
-
-Structuring the Application
----------------------------
-
-After creating the application, enter the ``blog/`` directory and you'll see a
-number of files and directories generated automatically:
-
-.. code-block:: text
-
- blog/
- ├─ app/
- │ ├─ config/
- │ └─ Resources/
- ├─ bin
- │ └─ console
- ├─ src/
- │ └─ AppBundle/
- ├─ var/
- │ ├─ cache/
- │ ├─ logs/
- │ └─ sessions/
- ├─ tests/
- │ └─ AppBundle/
- ├─ vendor/
- └─ web/
-
-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.
-
-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,
-ProductBundle, InvoiceBundle, etc.
-
-But a bundle is *meant* to be something that can be reused as a stand-alone
-piece of software. If UserBundle cannot be used *"as is"* in other Symfony
-apps, then it shouldn't be its own bundle. Moreover, if InvoiceBundle depends on
-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
----------------------------------
-
-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/``.
-
-.. _`Composer`: https://getcomposer.org/
-.. _`Phar extension`: http://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
diff --git a/best_practices/forms.rst b/best_practices/forms.rst
deleted file mode 100644
index a799d606ac4..00000000000
--- a/best_practices/forms.rst
+++ /dev/null
@@ -1,210 +0,0 @@
-Forms
-=====
-
-Forms are one of the most misused Symfony components due to its vast scope and
-endless list of features. In this chapter we'll show you some of the best
-practices so you can leverage forms but get work done quickly.
-
-Building Forms
---------------
-
-.. best-practice::
-
- 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::
-
- namespace AppBundle\Form;
-
- use AppBundle\Entity\Post;
- use Symfony\Component\Form\AbstractType;
- use Symfony\Component\Form\FormBuilderInterface;
- use Symfony\Component\OptionsResolver\OptionsResolver;
- use Symfony\Component\Form\Extension\Core\Type\TextareaType;
- use Symfony\Component\Form\Extension\Core\Type\EmailType;
- use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
-
- class PostType extends AbstractType
- {
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- $builder
- ->add('title')
- ->add('summary', TextareaType::class)
- ->add('content', TextareaType::class)
- ->add('authorEmail', EmailType::class)
- ->add('publishedAt', DateTimeType::class)
- ;
- }
-
- public function configureOptions(OptionsResolver $resolver)
- {
- $resolver->setDefaults(array(
- 'data_class' => Post::class,
- ));
- }
- }
-
-.. best-practice::
-
- Put the form type classes in the ``AppBundle\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;
-
- // ...
- public function newAction(Request $request)
- {
- $post = new Post();
- $form = $this->createForm(PostType::class, $post);
-
- // ...
- }
-
-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
--------------------------
-
-Form classes should try to be agnostic to *where* they will be used. This
-makes them easier to re-use later.
-
-.. best-practice::
-
- Add buttons in the templates, not in the form classes or the controllers.
-
-The Symfony Form component allows you to add buttons as fields on your form.
-This is a nice way to simplify the template that renders your form. But if you
-add the buttons directly in your form class, this would effectively limit the
-scope of that form:
-
-.. code-block:: php
-
- class PostType extends AbstractType
- {
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- $builder
- // ...
- ->add('save', SubmitType::class, array('label' => 'Create Post'))
- ;
- }
-
- // ...
- }
-
-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;
-
- 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)
- {
- $post = new Post();
- $form = $this->createForm(PostType::class, $post);
- $form->add('submit', SubmitType::class, array(
- 'label' => 'Create',
- 'attr' => array('class' => 'btn btn-default pull-right')
- ));
-
- // ...
- }
- }
-
-This is also an important error, because you are mixing presentation markup
-(labels, CSS classes, etc.) with pure PHP code. Separation of concerns is
-always a good practice to follow, so put all the view-related things in the
-view layer:
-
-.. code-block:: html+twig
-
- {{ form_start(form) }}
- {{ form_widget(form) }}
-
-
- {{ form_end(form) }}
-
-Rendering the Form
-------------------
-
-There are a lot of ways to render your form, ranging from rendering the entire
-thing in one line to rendering each part of each field independently. The
-best way depends on how much customization you need.
-
-One of the simplest ways - which is especially useful during development -
-is to render the form tags and use the ``form_widget()`` function to render
-all of the fields:
-
-.. code-block:: html+twig
-
- {{ form_start(form, {'attr': {'class': 'my-form-class'} }) }}
- {{ form_widget(form) }}
- {{ form_end(form) }}
-
-If you need more control over how your fields are rendered, then you should
-remove the ``form_widget(form)`` function and render your fields individually.
-See :doc:`/form/form_customization` for more information on this and how you
-can control *how* the form renders at a global level using form theming.
-
-Handling Form Submits
----------------------
-
-Handling a form submit usually follows a similar template:
-
-.. code-block:: php
-
- public function newAction(Request $request)
- {
- // build the form ...
-
- $form->handleRequest($request);
-
- if ($form->isSubmitted() && $form->isValid()) {
- $em = $this->getDoctrine()->getManager();
- $em->persist($post);
- $em->flush();
-
- return $this->redirect($this->generateUrl(
- 'admin_post_show',
- array('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, we recommend using ``$form->isSubmitted()`` in the ``if`` statement
-for clarity. This isn't technically needed, since ``isValid()`` first calls
-``isSubmitted()``. But without this, the flow doesn't read well as it *looks*
-like the form is *always* processed (even on the GET request).
diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst
deleted file mode 100644
index 39473373df1..00000000000
--- a/best_practices/i18n.rst
+++ /dev/null
@@ -1,96 +0,0 @@
-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:
-
-.. code-block:: yaml
-
- # app/config/config.yml
- framework:
- # ...
- translator: { fallbacks: ['%locale%'] }
-
- # app/config/parameters.yml
- parameters:
- # ...
- locale: en
-
-Translation Source File Format
-------------------------------
-
-The Symfony Translation component supports lots of different translation
-formats: PHP, Qt, ``.po``, ``.mo``, JSON, CSV, INI, etc.
-
-.. best-practice::
-
- Use the XLIFF format for your translation files.
-
-Of all the available translation formats, only XLIFF and gettext have broad
-support in the tools used by professional translators. And since it's based
-on XML, you can validate XLIFF file contents as you write them.
-
-Symfony supports notes in XLIFF files, making them more user-friendly for
-translators. At the end, good translations are all about context, and these
-XLIFF notes allow you to define that context.
-
-.. tip::
-
- The Apache-licensed `JMSTranslationBundle`_ offers you a web interface for
- viewing and editing these translation files. It also has 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
-----------------
-
-.. best-practice::
-
- 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
-files.
-
-Keys should always describe their *purpose* and *not* their location. For
-example, if a form has a field with the label "Username", then a nice key
-would be ``label.username``, *not* ``edit_form.label.username``.
-
-Example Translation File
-------------------------
-
-Applying all the previous best practices, the sample translation file for
-English in the application would be:
-
-.. code-block:: xml
-
-
-
-
-
-
-
- title.post_list
- Post List
-
-
-
-
-
-.. _`JMSTranslationBundle`: https://github.com/schmittjoh/JMSTranslationBundle
diff --git a/best_practices/index.rst b/best_practices/index.rst
deleted file mode 100644
index 8df4abb1364..00000000000
--- a/best_practices/index.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-Official Symfony Best Practices
-===============================
-
-.. toctree::
- :hidden:
-
- introduction
- creating-the-project
- configuration
- business-logic
- controllers
- templates
- forms
- i18n
- security
- web-assets
- tests
-
-.. include:: /best_practices/map.rst.inc
diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst
deleted file mode 100644
index 4260b79fd1a..00000000000
--- a/best_practices/introduction.rst
+++ /dev/null
@@ -1,102 +0,0 @@
-.. index::
- single: Symfony Framework Best Practices
-
-The Symfony Framework Best Practices
-====================================
-
-The Symfony Framework is well-known for being *really* flexible and is used
-to build micro-sites, enterprise applications that handle billions of connections
-and even as the basis for *other* frameworks. Since its release in July 2011,
-the community has learned a lot about what's possible and how to do things *best*.
-
-These community resources - like blog posts or presentations - have created
-an unofficial set of recommendations for developing Symfony applications.
-Unfortunately, a lot of these recommendations are unneeded for web applications.
-Much of the time, they unnecessarily overcomplicate things and don't follow the
-original pragmatic philosophy of Symfony.
-
-What is this Guide About?
--------------------------
-
-This guide aims to fix that by describing the **best practices for developing
-web apps with the Symfony full-stack Framework**. These are best practices that
-fit the philosophy of the framework as envisioned by its original creator
-`Fabien Potencier`_.
-
-.. note::
-
- **Best practice** is a noun that means *"a well defined procedure that is
- known to produce near-optimum results"*. And that's exactly what this
- guide aims to provide. Even if you don't agree with every recommendation,
- we believe these will help you build great applications with less complexity.
-
-This guide is **specially suited** for:
-
-* Websites and web applications developed with the full-stack Symfony Framework.
-
-For other situations, this guide might be a good **starting point** that you can
-then **extend and fit to your specific needs**:
-
-* Bundles shared publicly to the Symfony community;
-* Advanced developers or teams who have created their own standards;
-* Some complex applications that have highly customized requirements;
-* Bundles that may be shared internally within a company.
-
-We know that old habits die hard and some of you will be shocked by some
-of these best practices. But by following these, you'll be able to develop
-apps faster, with less complexity and with the same or even higher quality.
-It's also a moving target that will continue to improve.
-
-Keep in mind that these are **optional recommendations** that you and your
-team may or may not follow to develop Symfony applications. If you want to
-continue using your own best practices and methodologies, you can of course
-do it. Symfony is flexible enough to adapt to your needs. That will never
-change.
-
-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.
-
-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
-Injection or front controllers. We'll solely focus on explaining how to do
-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:
-
-.. code-block:: terminal
-
- $ 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
-implementation details. Instead of developing the application step by step in
-this guide, you'll find selected snippets of code through the chapters.
-
-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:
-
-* Your existing applications are not wrong, they just follow another set of
- guidelines;
-* A full codebase refactorization is prone to introduce errors in your
- applications;
-* The amount of work spent on this could be better dedicated to improving
- your tests or adding features that provide real value to the end users.
-
-.. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot
-.. _`download and install`: https://symfony.com/download
diff --git a/best_practices/map.rst.inc b/best_practices/map.rst.inc
deleted file mode 100644
index f9dfd0c3e9d..00000000000
--- a/best_practices/map.rst.inc
+++ /dev/null
@@ -1,11 +0,0 @@
-* :doc:`/best_practices/introduction`
-* :doc:`/best_practices/creating-the-project`
-* :doc:`/best_practices/configuration`
-* :doc:`/best_practices/business-logic`
-* :doc:`/best_practices/controllers`
-* :doc:`/best_practices/templates`
-* :doc:`/best_practices/forms`
-* :doc:`/best_practices/i18n`
-* :doc:`/best_practices/security`
-* :doc:`/best_practices/web-assets`
-* :doc:`/best_practices/tests`
diff --git a/best_practices/security.rst b/best_practices/security.rst
deleted file mode 100644
index af004171334..00000000000
--- a/best_practices/security.rst
+++ /dev/null
@@ -1,393 +0,0 @@
-Security
-========
-
-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.
-
-Regardless of your needs, authentication is configured in ``security.yml``,
-primarily under the ``firewalls`` key.
-
-.. best-practice::
-
- Unless you have two legitimately different authentication systems and
- users (e.g. form login for the main site and a token system for your
- API only), we recommend having only *one* firewall entry with the ``anonymous``
- key enabled.
-
-Most applications only have one authentication system and one set of users.
-For this reason, you only need *one* firewall entry. There are exceptions
-of course, especially if you have separated web and API sections on your
-site. But the point is to keep things simple.
-
-Additionally, you should use the ``anonymous`` key under your firewall. If
-you need to require users to be logged in for different sections of your
-site (or maybe nearly *all* sections), use the ``access_control`` area.
-
-.. best-practice::
-
- Use the ``bcrypt`` encoder for encoding your users' passwords.
-
-If your users have a password, then we recommend encoding 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
-remain resistant to brute-force search attacks.
-
-With this in mind, here is the authentication setup from our application,
-which uses a login form to load users from the database:
-
-.. code-block:: yaml
-
- # app/config/security.yml
- security:
- encoders:
- AppBundle\Entity\User: bcrypt
-
- providers:
- database_users:
- entity: { class: AppBundle:User, property: username }
-
- firewalls:
- secured_area:
- pattern: ^/
- anonymous: true
- form_login:
- check_path: login
- login_path: login
-
- logout:
- path: security_logout
- target: homepage
-
- # ... access_control exists, but is not shown here
-
-.. tip::
-
- The source code for our project contains comments that explain each part.
-
-Authorization (i.e. Denying Access)
------------------------------------
-
-Symfony gives you several ways to enforce authorization, including the ``access_control``
-configuration in :doc:`security.yml `, the
-:ref:`@Security annotation ` and using
-:ref:`isGranted ` on the ``security.authorization_checker``
-service directly.
-
-.. best-practice::
-
- * 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.
-
-There are also different ways to centralize your authorization logic, like
-with a custom security voter or with ACL.
-
-.. 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.
-
-.. _best-practices-security-annotation:
-
-The @Security Annotation
-------------------------
-
-For controlling access on a controller-by-controller basis, use the ``@Security``
-annotation whenever possible. It's easy to read and is placed consistently
-above each action.
-
-In our application, you need the ``ROLE_ADMIN`` in order to create a new post.
-Using ``@Security``, this looks like:
-
-.. code-block:: php
-
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
- // ...
-
- /**
- * Displays a form to create a new Post entity.
- *
- * @Route("/new", name="admin_post_new")
- * @Security("has_role('ROLE_ADMIN')")
- */
- public function newAction()
- {
- // ...
- }
-
-Using Expressions for Complex Security Restrictions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If your security logic is a little bit more complex, you can use an :doc:`expression `
-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:
-
-.. code-block:: php
-
- use AppBundle\Entity\Post;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
-
- /**
- * @Route("/{id}/edit", name="admin_post_edit")
- * @Security("user.getEmail() == post.getAuthorEmail()")
- */
- public function editAction(Post $post)
- {
- // ...
- }
-
-Notice that this requires the use of the `ParamConverter`_, which automatically
-queries for the ``Post`` object and puts it on the ``$post`` argument. This
-is what makes it possible to use the ``post`` variable in the expression.
-
-This has one major drawback: an expression in an annotation cannot easily
-be reused in other parts of the application. Imagine that you want to add
-a link in a template that will only be seen by authors. Right now you'll
-need to repeat the expression code using Twig syntax:
-
-.. code-block:: html+jinja
-
- {% if app.user and app.user.email == post.authorEmail %}
- ...
- {% endif %}
-
-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:
-
-.. code-block:: php
-
- // src/AppBundle/Entity/Post.php
- // ...
-
- class Post
- {
- // ...
-
- /**
- * Is the given User the author of this Post?
- *
- * @return bool
- */
- public function isAuthor(User $user = null)
- {
- return $user && $user->getEmail() == $this->getAuthorEmail();
- }
- }
-
-Now you can reuse this method both in the template and in the security expression:
-
-.. code-block:: php
-
- use AppBundle\Entity\Post;
- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
-
- /**
- * @Route("/{id}/edit", name="admin_post_edit")
- * @Security("post.isAuthor(user)")
- */
- public function editAction(Post $post)
- {
- // ...
- }
-
-.. code-block:: html+jinja
-
- {% if post.isAuthor(app.user) %}
- ...
- {% endif %}
-
-.. _best-practices-directly-isGranted:
-.. _checking-permissions-without-security:
-.. _manually-checking-permissions:
-
-Checking Permissions without @Security
---------------------------------------
-
-The above example with ``@Security`` only works because we're using the
-:ref:`ParamConverter `, which gives the expression
-access to the ``post`` variable. If you don't use this, or have some other
-more advanced use-case, you can always do the same security check in PHP:
-
-.. code-block:: php
-
- /**
- * @Route("/{id}/edit", name="admin_post_edit")
- */
- public function editAction($id, EntityManagerInterface $em)
- {
- $post = $em->getRepository('AppBundle:Post')
- ->find($id);
-
- if (!$post) {
- throw $this->createNotFoundException();
- }
-
- if (!$post->isAuthor($this->getUser())) {
- $this->denyAccessUnlessGranted('edit', $post);
- }
- // equivalent code without using the "denyAccessUnlessGranted()" shortcut:
- //
- // use Symfony\Component\Security\Core\Exception\AccessDeniedException;
- // ...
- //
- // if (!$this->get('security.authorization_checker')->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.
-
-First, create a voter class. The following example shows a voter that implements
-the same ``getAuthorEmail()`` logic you used above:
-
-.. code-block:: php
-
- namespace AppBundle\Security;
-
- 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)
- {
- $this->decisionManager = $decisionManager;
- }
-
- protected function supports($attribute, $subject)
- {
- if (!in_array($attribute, array(self::CREATE, self::EDIT))) {
- return false;
- }
-
- if (!$subject instanceof Post) {
- return false;
- }
-
- return true;
- }
-
- protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
- {
- $user = $token->getUser();
- /** @var Post */
- $post = $subject; // $subject must be a Post instance, thanks to the supports method
-
- if (!$user instanceof UserInterface) {
- return false;
- }
-
- switch ($attribute) {
- case self::CREATE:
- // if the user is an admin, allow them to create new posts
- if ($this->decisionManager->decide($token, array('ROLE_ADMIN'))) {
- return true;
- }
-
- break;
- 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;
- }
-
- break;
- }
-
- return false;
- }
- }
-
-If you're using the :ref:`default services.yml configuration `,
-your application will :ref:`autoconfigure ` your security
-voter and inject an ``AccessDecisionManagerInterface`` instance into it thanks to
-:doc:`autowiring `.
-
-Now, you can use the voter with the ``@Security`` annotation:
-
-.. code-block:: php
-
- /**
- * @Route("/{id}/edit", name="admin_post_edit")
- * @Security("is_granted('edit', post)")
- */
- public function editAction(Post $post)
- {
- // ...
- }
-
-You can also use this directly with the ``security.authorization_checker`` service or
-via the even easier shortcut in a controller:
-
-.. code-block:: php
-
- /**
- * @Route("/{id}/edit", name="admin_post_edit")
- */
- public function editAction($id)
- {
- $post = ...; // query for the post
-
- $this->denyAccessUnlessGranted('edit', $post);
-
- // or without the shortcut:
- //
- // use Symfony\Component\Security\Core\Exception\AccessDeniedException;
- // ...
- //
- // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) {
- // throw $this->createAccessDeniedException();
- // }
- }
-
-Learn More
-----------
-
-The `FOSUserBundle`_, developed by the Symfony community, adds support for a
-database-backed user system in Symfony. It also handles common tasks like
-user registration and forgotten password functionality.
-
-Enable the :doc:`Remember Me feature ` to
-allow your users to stay logged in for a long period of time.
-
-When providing customer support, sometimes it's necessary to access the application
-as some *other* user so that you can reproduce the problem. Symfony provides
-the ability to :doc:`impersonate users `.
-
-If your company uses a user login method not supported by Symfony, you can
-develop :doc:`your own user provider ` and
-:doc:`your own authentication provider `.
-
-.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
-.. _`@Security annotation`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html
-.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle
diff --git a/best_practices/templates.rst b/best_practices/templates.rst
deleted file mode 100644
index c9abd7f7902..00000000000
--- a/best_practices/templates.rst
+++ /dev/null
@@ -1,147 +0,0 @@
-Templates
-=========
-
-When PHP was created 20 years ago, developers loved its simplicity and how
-well it blended HTML and dynamic code. But as time passed, other template
-languages - like `Twig`_ - were created to make templating even better.
-
-.. best-practice::
-
- Use Twig templating format for your templates.
-
-Generally speaking, PHP templates are much 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.
-
-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 logical name
-to refer to them (e.g. ``AcmeDemoBundle: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/``
-================================================= ==================================
-``AcmeDemoBundle:Default:index.html.twig`` ``default/index.html.twig``
-``::layout.html.twig`` ``layout.html.twig``
-``AcmeDemoBundle::index.html.twig`` ``index.html.twig``
-``AcmeDemoBundle:Default:subdir/index.html.twig`` ``default/subdir/index.html.twig``
-``AcmeDemoBundle:Default/subdir:index.html.twig`` ``default/subdir/index.html.twig``
-================================================= ==================================
-
-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.
-
-.. best-practice::
-
- Use lowercased snake_case for directory and template names.
-
-Twig Extensions
----------------
-
-.. best-practice::
-
- Define your Twig extensions in the ``AppBundle/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
-
-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;
-
- class Markdown
- {
- private $parser;
-
- public function __construct()
- {
- $this->parser = new \Parsedown();
- }
-
- public function toHtml($text)
- {
- $html = $this->parser->text($text);
-
- return $html;
- }
- }
-
-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:
-
-.. code-block:: php
-
- namespace AppBundle\Twig;
-
- use AppBundle\Utils\Markdown;
-
- class AppExtension extends \Twig_Extension
- {
- private $parser;
-
- public function __construct(Markdown $parser)
- {
- $this->parser = $parser;
- }
-
- public function getFilters()
- {
- return array(
- new \Twig_SimpleFilter(
- 'md2html',
- array($this, 'markdownToHtml'),
- array('is_safe' => array('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 `,
-you're done! Symfony will automatically know about your new service and tag it to
-be used as a Twig extension.
-
-.. _`Twig`: http://twig.sensiolabs.org/
-.. _`Parsedown`: http://parsedown.org/
diff --git a/best_practices/tests.rst b/best_practices/tests.rst
deleted file mode 100644
index 1e9490d7aac..00000000000
--- a/best_practices/tests.rst
+++ /dev/null
@@ -1,123 +0,0 @@
-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.
-
-Unit Tests
-----------
-
-Unit tests are used to test your "business logic", which should live in classes
-that are independent of Symfony. For that reason, Symfony doesn't really
-have an opinion on what tools you use for unit testing. However, the most
-popular tools are `PhpUnit`_ and `PhpSpec`_.
-
-Functional Tests
-----------------
-
-Creating really good functional tests can be tough so some developers skip
-these completely. Don't skip the functional tests! By defining some *simple*
-functional tests, you can quickly spot any big errors before you deploy them:
-
-.. best-practice::
-
- Define a functional test that at least checks if your application pages
- are successfully loading.
-
-A functional test can be as easy as this::
-
- // tests/AppBundle/ApplicationAvailabilityFunctionalTest.php
- namespace Tests\AppBundle;
-
- use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
-
- class ApplicationAvailabilityFunctionalTest extends WebTestCase
- {
- /**
- * @dataProvider urlProvider
- */
- public function testPageIsSuccessful($url)
- {
- $client = self::createClient();
- $client->request('GET', $url);
-
- $this->assertTrue($client->getResponse()->isSuccessful());
- }
-
- public function urlProvider()
- {
- return array(
- array('/'),
- array('/posts'),
- array('/post/fixture-post-1'),
- array('/blog/category/fixture-category'),
- array('/archives'),
- // ...
- );
- }
- }
-
-This code checks that all the given URLs load successfully, which means that
-their HTTP response status code is between ``200`` and ``299``. This may
-not look that useful, but given how little effort this took, it's worth
-having it in your application.
-
-In computer software, this kind of test is called `smoke testing`_ and consists
-of *"preliminary testing to reveal simple failures severe enough to reject a
-prospective software release"*.
-
-Hardcode URLs in a Functional Test
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Some of you may be asking why the previous functional test doesn't use the URL
-generator service:
-
-.. best-practice::
-
- Hardcode the URLs used in the functional tests instead of using the URL
- generator.
-
-Consider the following functional test that uses the ``router`` service to
-generate the URL of the tested page:
-
-.. code-block:: php
-
- public function testBlogArchives()
- {
- $client = self::createClient();
- $url = $client->getContainer()->get('router')->generate('blog_archives');
- $client->request('GET', $url);
-
- // ...
- }
-
-This will work, but it has one *huge* drawback. If a developer mistakenly
-changes the path of the ``blog_archives`` route, the test will still pass,
-but the original (old) URL won't work! This means that any bookmarks for
-that URL will be broken and you'll lose any search engine page ranking.
-
-Testing JavaScript Functionality
---------------------------------
-
-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
-pure JavaScript-based testing tools.
-
-Learn More about Functional Tests
----------------------------------
-
-Consider using the `HautelookAliceBundle`_ to generate real-looking data for
-your test fixtures using `Faker`_ and `Alice`_.
-
-.. _`PhpUnit`: https://phpunit.de/
-.. _`PhpSpec`: http://www.phpspec.net/
-.. _`smoke testing`: https://en.wikipedia.org/wiki/Smoke_testing_(software)
-.. _`Mink`: http://mink.behat.org
-.. _`HautelookAliceBundle`: https://github.com/hautelook/AliceBundle
-.. _`Faker`: https://github.com/fzaninotto/Faker
-.. _`Alice`: https://github.com/nelmio/alice
diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst
deleted file mode 100644
index 7c3fe8a7ee0..00000000000
--- a/best_practices/web-assets.rst
+++ /dev/null
@@ -1,98 +0,0 @@
-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.
-
-.. best-practice::
-
- Store your assets in the ``web/`` directory.
-
-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.
-
-.. 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).
-
-Learn More about Assetic
-------------------------
-
-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
-features.
-
-.. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic
diff --git a/bundles.rst b/bundles.rst
index d4d692c724d..878bee3af4a 100644
--- a/bundles.rst
+++ b/bundles.rst
@@ -1,184 +1,171 @@
-.. index::
- single: Bundles
-
.. _page-creation-bundles:
The Bundle System
=================
-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::
+.. warning::
- While you'll learn the basics here, an entire article is devoted to the
- organization and best practices of :doc:`bundles `.
+ In Symfony versions prior to 4.0, it was recommended to organize your own
+ application code using bundles. This is :ref:`no longer recommended ` and bundles
+ should only be used to share code and features between multiple applications.
-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.
+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 by registering them in
-the ``registerBundles()`` method of the ``AppKernel`` class::
+Bundles used in your applications must be enabled per
+:ref:`environment ` in the ``config/bundles.php``
+file::
- // 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();
- }
+ // config/bundles.php
+ return [
+ // 'all' means that the bundle is enabled for any Symfony environment
+ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
+ // ...
- return $bundles;
- }
+ // this bundle is enabled only in 'dev'
+ Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
+ // ...
-With the ``registerBundles()`` method, you have total control over which bundles
-are used by your application (including the core Symfony bundles).
+ // 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 :ref:`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
-----------------
-The Symfony Standard Edition comes with a handy task that creates a fully-functional
-bundle for you. Of course, creating a bundle by hand is pretty easy as well.
+This section creates and enables a new bundle to show there are only a few steps required.
+The new bundle is called AcmeBlogBundle, where the ``Acme`` portion is an example
+name that should be replaced by some "vendor" name that represents you or your
+organization (e.g. AbcBlogBundle for some company named ``Abc``).
-To show you how simple the bundle system is, create a new bundle called
-AcmeTestBundle and enable it.
+Start by creating a new class called ``AcmeBlogBundle``::
-.. tip::
-
- 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``).
-
-Start by creating a ``src/Acme/TestBundle/`` directory and adding a new file
-called ``AcmeTestBundle.php``::
-
- // src/Acme/TestBundle/AcmeTestBundle.php
- namespace Acme\TestBundle;
+ // src/AcmeBlogBundle.php
+ namespace Acme\BlogBundle;
- use Symfony\Component\HttpKernel\Bundle\Bundle;
+ use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
- class AcmeTestBundle extends Bundle
+ class AcmeBlogBundle extends AbstractBundle
{
}
+.. warning::
+
+ If your bundle must be compatible with previous Symfony versions you have to
+ extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle` instead.
+
.. tip::
- The name AcmeTestBundle follows the standard
- :ref:`Bundle naming conventions `. You could
- also choose to shorten the name of the bundle to simply TestBundle by naming
- this class TestBundle (and naming the file ``TestBundle.php``).
+ The name AcmeBlogBundle follows the standard
+ :ref:`Bundle naming conventions `. You could
+ also choose to shorten the name of the bundle to simply BlogBundle by naming
+ this class BlogBundle (and naming the file ``BlogBundle.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::
+of the bundle. Now that you've created the bundle, enable it::
- // app/AppKernel.php
- public function registerBundles()
- {
- $bundles = array(
- // ...
-
- // register your bundle
- new Acme\TestBundle\AcmeTestBundle(),
- );
+ // config/bundles.php
+ return [
// ...
+ Acme\BlogBundle\AcmeBlogBundle::class => ['all' => true],
+ ];
- return $bundles;
- }
+And while it doesn't do anything yet, AcmeBlogBundle is now ready to be used.
-And while it doesn't do anything yet, AcmeTestBundle is now ready to be used.
+.. _bundles-directory-structure:
-And as easy as this is, Symfony also provides a command-line interface for
-generating a basic bundle skeleton:
+Bundle Directory Structure
+--------------------------
-.. code-block:: terminal
+The directory structure of a bundle is meant to help to keep code consistent
+between all Symfony bundles. It follows a set of conventions, but is flexible
+to be adjusted if needed:
- $ php bin/console generate:bundle --namespace=Acme/TestBundle
+``assets/``
+ Contains the web asset sources like JavaScript and TypeScript files, CSS and
+ Sass files, but also images and other assets related to the bundle that are
+ not in ``public/`` (e.g. Stimulus controllers).
-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.
+``config/``
+ Houses configuration, including routing configuration (e.g. ``routes.php``).
-.. tip::
+``public/``
+ Contains web assets (images, compiled CSS and JavaScript files, etc.) and is
+ copied or symbolically linked into the project ``public/`` directory via the
+ ``assets:install`` console command.
- 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.
+``src/``
+ Contains all PHP classes related to the bundle logic (e.g. ``Controller/CategoryController.php``).
-Bundle Directory Structure
---------------------------
+``templates/``
+ Holds templates organized by controller name (e.g. ``category/show.html.twig``).
-The directory structure of a bundle is simple and flexible. By default, the
-bundle system follows a set of conventions that help to keep code consistent
-between all Symfony bundles. Take a look at AcmeDemoBundle, as it contains some
-of the most common elements of a bundle:
+``tests/``
+ Holds all tests for the bundle.
-``Controller/``
- Contains the controllers of the bundle (e.g. ``RandomController.php``).
+``translations/``
+ Holds translations organized by domain and locale (e.g. ``AcmeBlogBundle.en.xlf``).
-``DependencyInjection/``
- Holds certain Dependency Injection Extension classes, which may import service
- configuration, register compiler passes or more (this directory is not
- necessary).
+.. _bundles-legacy-directory-structure:
-``Resources/config/``
- Houses configuration, including routing configuration (e.g. ``routing.yml``).
+.. warning::
-``Resources/views/``
- Holds templates organized by controller name (e.g. ``Random/index.html.twig``).
+ The recommended bundle structure was changed in Symfony 5, read the
+ `Symfony 4.4 bundle documentation`_ for information about the old
+ structure.
-``Resources/public/``
- Contains web assets (images, stylesheets, etc) and is copied or symbolically
- linked into the project ``web/`` directory via the ``assets:install`` console
- command.
+ When using the new ``AbstractBundle`` class, the bundle defaults to the
+ new structure. Override the ``Bundle::getPath()`` method to change to
+ the old structure::
-``Tests/``
- Holds all tests for the bundle.
+ class AcmeBlogBundle extends AbstractBundle
+ {
+ public function getPath(): string
+ {
+ return __DIR__;
+ }
+ }
-A bundle can be as small or large as the feature it implements. It contains
-only the files you need and nothing else.
+.. tip::
-As you move through the guides, you'll learn how to persist objects to a
-database, create and validate forms, create translations for your application,
-write tests and much more. Each of these has their own place and role within
-the bundle.
+ It's recommended to use the `PSR-4`_ autoload standard: use the namespace as key,
+ and the location of the bundle's main class (relative to ``composer.json``)
+ as value. As the main class is located in the ``src/`` directory of the bundle:
+
+ .. code-block:: json
+
+ {
+ "autoload": {
+ "psr-4": {
+ "Acme\\BlogBundle\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Acme\\BlogBundle\\Tests\\": "tests/"
+ }
+ }
+ }
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
+.. _`third-party bundles`: https://github.com/search?q=topic%3Asymfony-bundle&type=Repositories
+.. _`Symfony 4.4 bundle documentation`: https://symfony.com/doc/4.4/bundles.html#bundle-directory-structure
+.. _`PSR-4`: https://www.php-fig.org/psr/psr-4/
diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst
index 8cd849c5c47..023b58af162 100644
--- a/bundles/best_practices.rst
+++ b/bundles/best_practices.rst
@@ -1,43 +1,27 @@
-.. index::
- single: Bundle; Best practices
-
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`.
-
-.. index::
- pair: Bundle; Naming conventions
+This article is all about how to structure your **reusable bundles** to be
+configurable and extendable. Reusable bundles are those meant to be shared
+privately across many company projects or publicly so any Symfony project can
+install them.
.. _bundles-naming-conventions:
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 a ``Bundle`` suffix.
+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:
+A namespace becomes a bundle as soon as you add "a bundle class" to it (which is
+a class that extends :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle`).
+The bundle class name must follow these rules:
* Use only alphanumeric characters and underscores;
-* Use a CamelCased name;
+* Use a StudlyCaps name (i.e. camelCase with an uppercase first letter);
* Use a descriptive and short name (no more than two words);
* Prefix the name with the concatenation of the vendor (and optionally the
category namespaces);
@@ -48,8 +32,8 @@ Here are some valid bundle namespaces and class names:
========================== ==================
Namespace Bundle Class Name
========================== ==================
-``Acme\Bundle\BlogBundle`` ``AcmeBlogBundle``
-``Acme\BlogBundle`` ``AcmeBlogBundle``
+``Acme\Bundle\BlogBundle`` AcmeBlogBundle
+``Acme\BlogBundle`` AcmeBlogBundle
========================== ==================
By convention, the ``getName()`` method of the bundle class should return the
@@ -58,8 +42,7 @@ class name.
.. note::
If you share your bundle publicly, you must use the bundle class name as
- the name of the repository (``AcmeBlogBundle`` and not ``BlogBundle``
- for instance).
+ the name of the repository (AcmeBlogBundle and not BlogBundle for instance).
.. note::
@@ -68,45 +51,64 @@ class name.
:class:`Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle`.
Each bundle has an alias, which is the lower-cased short version of the bundle
-name using underscores (``acme_blog`` for ``AcmeBlogBundle``). This alias
+name using underscores (``acme_blog`` for AcmeBlogBundle). This alias
is used to enforce uniqueness within a project and for defining bundle's
configuration options (see below for some usage examples).
Directory Structure
-------------------
-The basic directory structure of an AcmeBlogBundle must read as follows:
+The following is the recommended directory structure of an AcmeBlogBundle:
.. code-block:: text
/
- ├─ AcmeBlogBundle.php
- ├─ Controller/
- ├─ README.md
- ├─ LICENSE
- ├─ Resources/
- │ ├─ config/
- │ ├─ doc/
- │ │ └─ index.rst
- │ ├─ translations/
- │ ├─ views/
- │ └─ public/
- └─ Tests/
+ ├── assets/
+ ├── config/
+ ├── docs/
+ │ └─ index.md
+ ├── public/
+ ├── src/
+ │ ├── Controller/
+ │ ├── DependencyInjection/
+ │ └── AcmeBlogBundle.php
+ ├── templates/
+ ├── tests/
+ ├── translations/
+ ├── LICENSE
+ └── README.md
+
+.. note::
+
+ This directory structure is used by default when your bundle class extends
+ the recommended :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`.
+ If your bundle extends the :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle`
+ class, you have to override the ``getPath()`` method as follows::
+
+ use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+ class AcmeBlogBundle extends Bundle
+ {
+ public function getPath(): string
+ {
+ return \dirname(__DIR__);
+ }
+ }
**The following files are mandatory**, because they ensure a structure convention
that automated tools can rely on:
-* ``AcmeBlogBundle.php``: This is the class that transforms a plain directory
+* ``src/AcmeBlogBundle.php``: This is the class that transforms a plain directory
into a Symfony bundle (change this to your bundle's name);
* ``README.md``: This file contains the basic description of the bundle and it
usually shows some basic examples and links to its full documentation (it
can use any of the markup formats supported by GitHub, such as ``README.rst``);
* ``LICENSE``: The full contents of the license used by the code. Most third-party
bundles are published under the MIT license, but you can `choose any license`_;
-* ``Resources/doc/index.rst``: The root file for the Bundle documentation.
+* ``docs/index.md``: The root file for the Bundle documentation.
-The depth of sub-directories should be kept to the minimum for most used
-classes and files (two levels maximum).
+The depth of subdirectories should be kept to a minimum for the most used
+classes and files. Two levels is the maximum.
The bundle directory is read-only. If you need to write temporary files, store
them under the ``cache/`` or ``log/`` directory of the host application. Tools
@@ -116,31 +118,32 @@ files are going to be part of the repository.
The following classes and files have specific emplacements (some are mandatory
and others are just conventions followed by most developers):
-=============================== ============================= ================
-Type Directory Mandatory?
-=============================== ============================= ================
-Commands ``Command/`` Yes
-Controllers ``Controller/`` No
-Service Container Extensions ``DependencyInjection/`` Yes
-Event Listeners ``EventListener/`` No
-Model classes [1] ``Model/`` No
-Configuration ``Resources/config/`` No
-Web Resources (CSS, JS, images) ``Resources/public/`` Yes
-Translation files ``Resources/translations/`` Yes
-Templates ``Resources/views/`` Yes
-Unit and Functional Tests ``Tests/`` No
-=============================== ============================= ================
-
-[1] See :doc:`/doctrine/mapping_model_classes` for how to handle the
-mapping with a compiler pass.
+=================================================== ========================================
+Type Directory
+=================================================== ========================================
+Commands ``src/Command/``
+Controllers ``src/Controller/``
+Service Container Extensions ``src/DependencyInjection/``
+Doctrine ORM entities ``src/Entity/``
+Doctrine ODM documents ``src/Document/``
+Event Listeners ``src/EventListener/``
+Configuration (routes, services, etc.) ``config/``
+Web Assets (compiled CSS and JS, images) ``public/``
+Web Asset sources (``.scss``, ``.ts``, Stimulus) ``assets/``
+Translation files ``translations/``
+Validation (when not using attributes) ``config/validation/``
+Serialization (when not using attributes) ``config/serialization/``
+Templates ``templates/``
+Unit and Functional Tests ``tests/``
+=================================================== ========================================
Classes
-------
The bundle directory structure is used as the namespace hierarchy. For
-instance, a ``ContentController`` controller is stored in
-``Acme/BlogBundle/Controller/ContentController.php`` and the fully qualified
-class name is ``Acme\BlogBundle\Controller\ContentController``.
+instance, a ``ContentController`` controller which is stored in
+``src/Controller/ContentController.php`` would have the fully
+qualified class name of ``Acme\BlogBundle\Controller\ContentController``.
All classes and files must follow the :doc:`Symfony coding standards `.
@@ -158,14 +161,23 @@ Vendors
A bundle must not embed third-party PHP libraries. It should rely on the
standard Symfony autoloading instead.
-A bundle should not embed third-party libraries written in JavaScript, CSS or
-any other language.
+A bundle should also not embed third-party libraries written in JavaScript,
+CSS or any other language.
+
+Doctrine Entities/Documents
+---------------------------
+
+If the bundle includes Doctrine ORM entities and/or ODM documents, it's
+recommended to define their mapping using XML files stored in
+``config/doctrine/``. This allows to override that mapping using the
+:doc:`standard Symfony mechanism to override bundle parts `.
+This is not possible when using attributes to define the mapping.
Tests
-----
A bundle should come with a test suite written with PHPUnit and stored under
-the ``Tests/`` directory. Tests should follow the following principles:
+the ``tests/`` directory. Tests should follow the following principles:
* The test suite must be executable with a simple ``phpunit`` command run from
a sample application;
@@ -175,18 +187,92 @@ the ``Tests/`` directory. Tests should follow the following principles:
.. note::
- A test suite must not contain ``AllTests.php`` scripts, but must rely on the
- existence of a ``phpunit.xml.dist`` file.
+ 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, like `GitHub Actions`_.
+
+A bundle should at least test:
+
+* The lower bound of their dependencies (by running ``composer update --prefer-lowest``);
+* The supported PHP versions;
+* All supported major Symfony versions (e.g. both ``6.4`` and ``7.x`` if
+ support is claimed for both).
+
+Thus, a bundle supporting PHP 7.4, 8.3 and 8.4, and Symfony 6.4 and 7.x should
+have at least this test matrix:
+
+=========== =============== ===================
+PHP version Symfony version Composer flags
+=========== =============== ===================
+7.4 ``6.4`` ``--prefer-lowest``
+8.3 ``7.*``
+8.4 ``7.*``
+=========== =============== ===================
+
+.. tip::
+
+ The tests should be run with the ``SYMFONY_DEPRECATIONS_HELPER``
+ env variable set to ``max[direct]=0``. This ensures no code in the
+ bundle uses deprecated features directly.
+
+ The lowest dependency tests can be run with this variable set to
+ ``disabled=1``.
+
+Require a Specific Symfony Version
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can use the special ``SYMFONY_REQUIRE`` environment variable together
+with Symfony Flex to install a specific Symfony version:
+
+.. code-block:: bash
+
+ # this requires Symfony 7.x for all Symfony packages
+ export SYMFONY_REQUIRE=7.*
+ # alternatively you can run this command to update composer.json config
+ # composer config extra.symfony.require "7.*"
+
+ # install Symfony Flex in the CI environment
+ composer global config --no-plugins allow-plugins.symfony/flex true
+ composer global require --no-progress --no-scripts --no-plugins symfony/flex
+
+ # install the dependencies (using --prefer-dist and --no-progress is
+ # recommended to have a better output and faster download time)
+ composer update --prefer-dist --no-progress
+
+.. warning::
+
+ If you want to cache your Composer dependencies, **do not** cache the
+ ``vendor/`` directory as this has side-effects. Instead cache
+ ``$HOME/.composer/cache/files``.
+
+Installation
+------------
+
+Bundles should set ``"type": "symfony-bundle"`` in their ``composer.json`` file.
+With this, :ref:`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
-------------
All classes and functions must come with full PHPDoc.
-Extensive documentation should also be provided in the
-:doc:`reStructuredText ` format, under
-the ``Resources/doc/`` directory; the ``Resources/doc/index.rst`` file is
-the only mandatory file and must be the entry point for the documentation.
+Extensive documentation should also be provided in the ``docs/``
+directory.
+The index file (for example ``docs/index.rst`` or
+``docs/index.md``) is the only mandatory file and must be the entry
+point for the documentation. The
+:doc:`reStructuredText (rST) ` is the format
+used to render the documentation on the Symfony website.
Installation Instructions
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -201,46 +287,43 @@ following standardized instructions in your ``README.md`` file.
Installation
============
- Step 1: Download the Bundle
- ---------------------------
+ Make sure Composer is installed globally, as explained in the
+ [installation chapter](https://getcomposer.org/doc/00-intro.md)
+ of the Composer documentation.
- Open a command console, enter your project directory and execute the
- following command to download the latest stable version of this bundle:
+ Applications that use Symfony Flex
+ ----------------------------------
+
+ Open a command console, enter your project directory and execute:
```console
- $ composer require "~1"
+ composer require
```
- This command requires you to have Composer installed globally, as explained
- in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
- of the Composer documentation.
+ Applications that don't use Symfony Flex
+ ----------------------------------------
- Step 2: Enable the Bundle
- -------------------------
+ ### Step 1: Download the Bundle
- Then, enable the bundle by adding it to the list of registered bundles
- in the `app/AppKernel.php` file of your project:
+ Open a command console, enter your project directory and execute the
+ following command to download the latest stable version of this bundle:
- ```php
-
+ ```
- // ...
- class AppKernel extends Kernel
- {
- public function registerBundles()
- {
- $bundles = array(
- // ...
+ ### Step 2: Enable the Bundle
- new \\(),
- );
+ Then, enable the bundle by adding it to the list of registered bundles
+ in the `config/bundles.php` file of your project:
- // ...
- }
+ ```php
+ // config/bundles.php
+ return [
// ...
- }
+ \\::class => ['all' => true],
+ ];
```
.. code-block:: rst
@@ -248,46 +331,41 @@ following standardized instructions in your ``README.md`` file.
Installation
============
- Step 1: Download the Bundle
- ---------------------------
+ Make sure Composer is installed globally, as explained in the
+ `installation chapter`_ of the Composer documentation.
- Open a command console, enter your project directory and execute the
- following command to download the latest stable version of this bundle:
+ ----------------------------------
+
+ Open a command console, enter your project directory and execute:
.. code-block:: terminal
- $ composer require "~1"
+ composer require
- This command requires you to have Composer installed globally, as explained
- in the `installation chapter`_ of the Composer documentation.
+ Applications that don't use Symfony Flex
+ ----------------------------------------
- Step 2: Enable the Bundle
- -------------------------
+ Step 1: Download the Bundle
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Then, enable the bundle by adding it to the list of registered bundles
- in the ``app/AppKernel.php`` file of your project:
+ Open a command console, enter your project directory and execute the
+ following command to download the latest stable version of this bundle:
- .. code-block:: php
+ .. code-block:: terminal
-
- // ...
- class AppKernel extends Kernel
- {
- public function registerBundles()
- {
- $bundles = array(
- // ...
-
- new \\(),
- );
+ Step 2: Enable the Bundle
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
- // ...
- }
+ Then, enable the bundle by adding it to the list of registered bundles
+ in the ``config/bundles.php`` file of your project::
+ // config/bundles.php
+ return [
// ...
- }
+ \\::class => ['all' => true],
+ ];
.. _`installation chapter`: https://getcomposer.org/doc/00-intro.md
@@ -318,10 +396,14 @@ Translation Files
-----------------
If a bundle provides message translations, they must be defined in the XLIFF
-format; the domain should be named after the bundle name (``acme_blog``).
+format; the domain should be named after the bundle name (``AcmeBlog``).
A bundle must not override existing messages from another bundle.
+The translation domain must match the translation file names. For example,
+if the translation domain is ``AcmeBlog``, the English translation file name
+should be ``AcmeBlog.en.xlf``.
+
Configuration
-------------
@@ -341,19 +423,19 @@ 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
-
+
-
+ https://symfony.com/schema/dic/services/services-1.0.xsd"
+ >
fabien@example.com
@@ -362,15 +444,22 @@ The end user can provide values in any configuration file:
.. code-block:: php
- // app/config/config.php
- $container->setParameter('acme_blog.author.email', 'fabien@example.com');
+ // config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ return static function (ContainerConfigurator $container): void {
+ $container->parameters()
+ ->set('acme_blog.author.email', 'fabien@example.com')
+ ;
+ };
Retrieve the configuration parameters in your code from the container::
$container->getParameter('acme_blog.author.email');
-Even if this mechanism is simple enough, you should consider using the more
-advanced :doc:`semantic bundle configuration `.
+While this mechanism requires the least effort, you should consider using the
+more advanced :doc:`semantic bundle configuration ` to
+make your configuration more robust.
Versioning
----------
@@ -380,8 +469,11 @@ Bundles must be versioned following the `Semantic Versioning Standard`_.
Services
--------
-If the bundle defines services, they must be prefixed with the bundle alias.
-For example, AcmeBlogBundle services must be prefixed with ``acme_blog``.
+If the bundle defines services, they must be prefixed with the bundle alias
+instead of using fully qualified class names like you do in your project
+services. For example, AcmeBlogBundle services must be prefixed with ``acme_blog``.
+The reason is that bundles shouldn't rely on features such as service autowiring
+or autoconfiguration to not impose an overhead when compiling application services.
In addition, services not meant to be used by the application directly, should
be :ref:`defined as private `. For public services,
@@ -393,6 +485,13 @@ can be used for autowiring.
Services should not use autowiring or autoconfiguration. Instead, all services should
be defined explicitly.
+.. tip::
+
+ If there is no intention for the service id to be used by the end user, you can
+ mark it as *hidden* by prefixing it with a dot (e.g. ``.acme_blog.logger``).
+ This prevents the service from being listed in the default ``debug:container``
+ command output.
+
.. seealso::
You can learn much more about service loading in bundles reading this article:
@@ -406,9 +505,9 @@ The ``composer.json`` file should include at least the following metadata:
``name``
Consists of the vendor and the short bundle name. If you are releasing the
bundle on your own instead of on behalf of a company, use your personal name
- (e.g. ``johnsmith/blog-bundle``). The bundle short name excludes the vendor
- name and separates each word with an hyphen. For example: ``AcmeBlogBundle``
- is transformed into ``blog-bundle`` and ``AcmeSocialConnectBundle`` is
+ (e.g. ``johnsmith/blog-bundle``). Exclude the vendor name from the bundle
+ short name and separate each word with a hyphen. For example: AcmeBlogBundle
+ is transformed into ``blog-bundle`` and AcmeSocialConnectBundle is
transformed into ``social-connect-bundle``.
``description``
@@ -418,25 +517,53 @@ The ``composer.json`` file should include at least the following metadata:
Use the ``symfony-bundle`` value.
``license``
- ``MIT`` is the preferred license for Symfony bundles, but you can use any
- other license.
+ 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: use the namespace as key,
+ and the location of the bundle's main class (relative to ``composer.json``)
+ as value. As the main class is located in the ``src/`` directory of the bundle:
+
+ .. code-block:: json
+
+ {
+ "autoload": {
+ "psr-4": {
+ "Acme\\BlogBundle\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Acme\\BlogBundle\\Tests\\": "tests/"
+ }
+ }
+ }
In order to make it easier for developers to find your bundle, register it on
`Packagist`_, the official repository for Composer packages.
+Resources
+---------
+
+If the bundle references any resources (config files, translation files, etc.),
+you can use physical paths (e.g. ``__DIR__/config/services.xml``).
+
+In the past, we recommended to only use logical paths (e.g.
+``@AcmeBlogBundle/config/services.xml``) and resolve them with the
+:ref:`resource locator ` provided by the Symfony
+kernel, but this is no longer a recommended practice.
+
Learn more
----------
* :doc:`/bundles/extension`
* :doc:`/bundles/configuration`
-.. _`PSR-0`: http://www.php-fig.org/psr/psr-0/
-.. _`PSR-4`: http://www.php-fig.org/psr/psr-4/
-.. _`Semantic Versioning Standard`: http://semver.org/
+.. _`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`: http://choosealicense.com/
+.. _`choose any license`: https://choosealicense.com/
+.. _`valid license identifier`: https://spdx.org/licenses/
+.. _`GitHub Actions`: https://docs.github.com/en/free-pro-team@latest/actions
diff --git a/bundles/configuration.rst b/bundles/configuration.rst
index 081e7df682a..dedfada2ea2 100644
--- a/bundles/configuration.rst
+++ b/bundles/configuration.rst
@@ -1,15 +1,12 @@
-.. index::
- single: Configuration; Semantic
- single: Bundle; Extension configuration
-
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
@@ -19,59 +16,147 @@ as integration of other related components:
.. code-block:: yaml
+ # config/packages/framework.yaml
framework:
form: true
.. code-block:: xml
+
-
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"
+ >
-
+
.. code-block:: php
- $container->loadFromExtension('framework', array(
- 'form' => true,
- ));
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->form()->enabled(true);
+ };
+
+There are two different ways of creating friendly configuration for a bundle:
+
+#. :ref:`Using the main bundle class `:
+ this is recommended for new bundles and for bundles following the
+ :ref:`recommended directory structure `;
+#. :ref:`Using the Bundle extension class `:
+ this was the traditional way of doing it, but nowadays it's only recommended for
+ bundles following the :ref:`legacy directory structure `.
+
+.. _using-the-bundle-class:
+.. _bundle-friendly-config-bundle-class:
+
+Using the AbstractBundle Class
+------------------------------
+
+In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
+class, you can add all the logic related to processing the configuration in that class::
+
+ // src/AcmeSocialBundle.php
+ namespace Acme\SocialBundle;
+
+ use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+ use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
+
+ class AcmeSocialBundle extends AbstractBundle
+ {
+ public function configure(DefinitionConfigurator $definition): void
+ {
+ $definition->rootNode()
+ ->children()
+ ->arrayNode('twitter')
+ ->children()
+ ->integerNode('client_id')->end()
+ ->scalarNode('client_secret')->end()
+ ->end()
+ ->end() // twitter
+ ->end()
+ ;
+ }
+
+ public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
+ {
+ // the "$config" variable is already merged and processed so you can
+ // use it directly to configure the service container (when defining an
+ // extension class, you also have to do this merging and processing)
+ $container->services()
+ ->get('acme_social.twitter_client')
+ ->arg(0, $config['twitter']['client_id'])
+ ->arg(1, $config['twitter']['client_secret'])
+ ;
+ }
+ }
-.. sidebar:: Using Parameters to Configure your Bundle
+.. note::
- 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.
+ The ``configure()`` and ``loadExtension()`` methods are called only at compile 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.
+.. tip::
+
+ The ``AbstractBundle::configure()`` method also allows to import the
+ configuration definition from one or more files::
+
+ // src/AcmeSocialBundle.php
+ namespace Acme\SocialBundle;
+
+ // ...
+ class AcmeSocialBundle extends AbstractBundle
+ {
+ public function configure(DefinitionConfigurator $definition): void
+ {
+ $definition->import('../config/definition.php');
+ // you can also use glob patterns
+ //$definition->import('../config/definition/*.php');
+ }
+
+ // ...
+ }
+
+ .. code-block:: php
+
+ // config/definition.php
+ use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
+
+ return static function (DefinitionConfigurator $definition): void {
+ $definition->rootNode()
+ ->children()
+ ->scalarNode('foo')->defaultValue('bar')->end()
+ ->end()
+ ;
+ };
+
+.. _bundle-friendly-config-extension:
Using the Bundle Extension
--------------------------
-The basic idea is that instead of having the user override individual
-parameters, you let the user configure just a few, specifically created,
-options. As the bundle developer, you then parse through that configuration and
-load correct services and parameters inside an "Extension" class.
+This is the traditional way of creating friendly configuration for bundles. For new
+bundles it's recommended to :ref:`use the main bundle class `,
+but the traditional way of creating an extension class still works.
-As an example, imagine you are creating a social bundle, which provides
-integration with Twitter and such. To be able to reuse your bundle, you have to
-make the ``client_id`` and ``client_secret`` variables configurable. Your
-bundle configuration would look like:
+Imagine you are creating a new bundle - AcmeSocialBundle - which provides
+integration with X/Twitter. To make your bundle configurable to the user, you
+can add some configuration that looks like this:
.. configuration-block::
.. code-block:: yaml
- # app/config/config.yml
+ # config/packages/acme_social.yaml
acme_social:
twitter:
client_id: 123
@@ -79,28 +164,42 @@ bundle configuration would look like:
.. code-block:: xml
-
-
+
+
-
-
-
-
-
-
+ https://symfony.com/schema/dic/services/services-1.0.xsd"
+ >
+
+
+
.. code-block:: php
- // app/config/config.php
- $container->loadFromExtension('acme_social', array(
- 'client_id' => 123,
- 'client_secret' => 'your_secret',
- ));
+ // config/packages/acme_social.php
+ use Symfony\Config\AcmeSocialConfig;
+
+ return static function (AcmeSocialConfig $acmeSocial): void {
+ $acmeSocial->twitter()
+ ->clientId(123)
+ ->clientSecret('your_secret');
+ };
+
+The basic idea is that instead of having the user override individual
+parameters, you let the user configure just a few, specifically created,
+options. As the bundle developer, you then parse through that configuration and
+load correct services and parameters inside an "Extension" class.
+
+.. note::
+
+ The root key of your bundle configuration (``acme_social`` in the previous
+ example) is automatically determined from your bundle name (it's the
+ `snake case`_ of the bundle name without the ``Bundle`` suffix).
.. seealso::
@@ -110,7 +209,7 @@ bundle configuration would look like:
If a bundle provides an Extension class, then you should *not* generally
override any service container parameters from that bundle. The idea
- is that if an Extension class is present, every setting that should be
+ is that if an extension class is present, every setting that should be
configurable should be present in the configuration made available by
that class. In other words, the extension class defines all the public
configuration settings for which backward compatibility will be maintained.
@@ -120,7 +219,6 @@ bundle configuration would look like:
For parameter handling within a dependency injection container see
:doc:`/configuration/using_parameters_in_dic`.
-
Processing the ``$configs`` Array
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -135,36 +233,36 @@ automatically converts XML and YAML to an array).
For the configuration example in the previous section, the array passed to your
``load()`` method will look like this::
- array(
- array(
- 'twitter' => array(
+ [
+ [
+ 'twitter' => [
'client_id' => 123,
'client_secret' => 'your_secret',
- ),
- ),
- )
+ ],
+ ],
+ ]
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::
-
- array(
- // values from config.yml
- array(
- 'twitter' => array(
+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::
+
+ [
+ // values from config/packages/acme_social.yaml
+ [
+ 'twitter' => [
'client_id' => 123,
'client_secret' => 'your_secret',
- ),
- ),
- // values from config_dev.yml
- array(
- 'twitter' => array(
+ ],
+ ],
+ // values from config/packages/dev/acme_social.yaml
+ [
+ 'twitter' => [
'client_id' => 456,
- ),
- ),
- )
+ ],
+ ],
+ ]
The order of the two arrays depends on which one is set first.
@@ -176,7 +274,7 @@ of your bundle's configuration.
The ``Configuration`` class to handle the sample configuration looks like::
- // src/Acme/SocialBundle/DependencyInjection/Configuration.php
+ // src/DependencyInjection/Configuration.php
namespace Acme\SocialBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
@@ -184,12 +282,11 @@ The ``Configuration`` class to handle the sample configuration looks like::
class Configuration implements ConfigurationInterface
{
- public function getConfigTreeBuilder()
+ public function getConfigTreeBuilder(): TreeBuilder
{
- $treeBuilder = new TreeBuilder();
- $rootNode = $treeBuilder->root('acme_social');
+ $treeBuilder = new TreeBuilder('acme_social');
- $rootNode
+ $treeBuilder->getRootNode()
->children()
->arrayNode('twitter')
->children()
@@ -218,9 +315,8 @@ This class can now be used in your ``load()`` method to merge configurations and
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)
+ // src/DependencyInjection/AcmeSocialExtension.php
+ public function load(array $configs, ContainerBuilder $container): void
{
$configuration = new Configuration();
@@ -239,15 +335,15 @@ For example, imagine your bundle has the following example config:
.. code-block:: xml
-
+
-
+ https://symfony.com/schema/dic/services/services-1.0.xsd"
+ >
-
+
@@ -256,13 +352,13 @@ For example, imagine your bundle has the following example config:
In your extension, you can load this and dynamically set its arguments::
- // src/Acme/SocialBundle/DependencyInjection/AcmeSocialExtension.php
- // ...
+ // src/DependencyInjection/AcmeSocialExtension.php
+ namespace Acme\SocialBundle\DependencyInjection;
- use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
+ use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
- public function load(array $configs, ContainerBuilder $container)
+ public function load(array $configs, ContainerBuilder $container): void
{
$loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources/config'));
$loader->load('services.xml');
@@ -270,12 +366,11 @@ In your extension, you can load this and dynamically set its arguments::
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
- $def = $container->getDefinition('acme.social.twitter_client');
- $def->replaceArgument(0, $config['twitter']['client_id']);
- $def->replaceArgument(1, $config['twitter']['client_secret']);
+ $definition = $container->getDefinition('acme_social.twitter_client');
+ $definition->replaceArgument(0, $config['twitter']['client_id']);
+ $definition->replaceArgument(1, $config['twitter']['client_secret']);
}
-
.. tip::
Instead of calling ``processConfiguration()`` in your extension each time you
@@ -283,7 +378,7 @@ In your extension, you can load this and dynamically set its arguments::
:class:`Symfony\\Component\\HttpKernel\\DependencyInjection\\ConfigurableExtension`
to do this automatically for you::
- // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ // src/DependencyInjection/HelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -292,7 +387,7 @@ In your extension, you can load this and dynamically set its arguments::
class AcmeHelloExtension extends ConfigurableExtension
{
// note that this method is called loadInternal and not load
- protected function loadInternal(array $mergedConfig, ContainerBuilder $container)
+ protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void
{
// ...
}
@@ -304,15 +399,13 @@ In your extension, you can load this and dynamically set its arguments::
.. sidebar:: Processing the Configuration yourself
Using the Config component is fully optional. The ``load()`` method gets an
- array of configuration values. You can simply parse these arrays yourself
+ array of configuration values. You can instead parse these arrays yourself
(e.g. by overriding configurations and using :phpfunction:`isset` to check
- for the existence of a value). Be aware that it'll be very hard to support XML.
+ for the existence of a value). Be aware that it'll be very hard to support XML::
- .. code-block:: php
-
- public function load(array $configs, ContainerBuilder $container)
+ public function load(array $configs, ContainerBuilder $container): void
{
- $config = array();
+ $config = [];
// let resources override the previous set value
foreach ($configs as $subConfig) {
$config = array_merge($config, $subConfig);
@@ -324,12 +417,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
----------------------
@@ -338,10 +429,10 @@ The ``config:dump-reference`` command dumps the default configuration of a
bundle in the console using the Yaml format.
As long as your bundle's configuration is located in the standard location
-(``YourBundle\DependencyInjection\Configuration``) and does not have
-a constructor it will work automatically. If you
+(``/src/DependencyInjection/Configuration``) and does not have
+a constructor, it will work automatically. If you
have something different, your ``Extension`` class must override the
-:method:`Extension::getConfiguration() `
+:method:`Extension::getConfiguration() `
method and return an instance of your ``Configuration``.
Supporting XML
@@ -368,18 +459,19 @@ In XML, the `XML namespace`_ is used to determine which elements belong to the
configuration of a specific bundle. The namespace is returned from the
:method:`Extension::getNamespace() `
method. By convention, the namespace is a URL (it doesn't have to be a valid
-URL nor does it need to exists). By default, the namespace for a bundle is
+URL nor does it need to exist). By default, the namespace for a bundle is
``http://example.org/schema/dic/DI_ALIAS``, where ``DI_ALIAS`` is the DI alias of
the extension. You might want to change this to a more professional URL::
- // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ // src/DependencyInjection/AcmeHelloExtension.php
+ namespace Acme\HelloBundle\DependencyInjection;
// ...
class AcmeHelloExtension extends Extension
{
// ...
- public function getNamespace()
+ public function getNamespace(): string
{
return 'http://acme_company.com/schema/dic/hello';
}
@@ -390,7 +482,7 @@ Providing an XML Schema
XML has a very useful feature called `XML schema`_. This allows you to
describe all possible elements and attributes and their values in an XML Schema
-Definition (an xsd file). This XSD file is used by IDEs for auto completion and
+Definition (an XSD file). This XSD file is used by IDEs for auto completion and
it is used by the Config component to validate the elements.
In order to use the schema, the XML configuration file must provide an
@@ -401,35 +493,38 @@ 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 ``config/schema/`` directory, but you
can place it anywhere you like. You should return this path as the base path::
- // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ // src/DependencyInjection/AcmeHelloExtension.php
+ namespace Acme\HelloBundle\DependencyInjection;
// ...
class AcmeHelloExtension extends Extension
{
// ...
- public function getXsdValidationBasePath()
+ public function getXsdValidationBasePath(): string
{
- return __DIR__.'/../Resources/config/schema';
+ return __DIR__.'/../config/schema';
}
}
Assuming the XSD file is called ``hello-1.0.xsd``, the schema location will be
-``http://acme_company.com/schema/dic/hello/hello-1.0.xsd``:
+``https://acme_company.com/schema/dic/hello/hello-1.0.xsd``:
.. code-block:: xml
-
-
+
+
-
+ xsi:schemaLocation="http://symfony.com/schema/dic/services
+ https://symfony.com/schema/dic/services/services-1.0.xsd
+ http://acme_company.com/schema/dic/hello
+ https://acme_company.com/schema/dic/hello/hello-1.0.xsd"
+ >
@@ -441,3 +536,4 @@ Assuming the XSD file is called ``hello-1.0.xsd``, the schema location will be
.. _`TwigBundle Configuration`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php
.. _`XML namespace`: https://en.wikipedia.org/wiki/XML_namespace
.. _`XML schema`: https://en.wikipedia.org/wiki/XML_schema
+.. _`snake case`: https://en.wikipedia.org/wiki/Snake_case
diff --git a/bundles/extension.rst b/bundles/extension.rst
index da6381ac9cb..d2792efc477 100644
--- a/bundles/extension.rst
+++ b/bundles/extension.rst
@@ -1,46 +1,102 @@
-.. index::
- single: Configuration; Semantic
- single: Bundle; Extension configuration
-
How to Load Service Configuration inside a Bundle
=================================================
-In Symfony, you'll find yourself using many services. These services can be
-registered in the ``app/config/`` directory of your application. But when you
-want to decouple the bundle for use in other projects, you want to include the
-service configuration in the bundle itself. This article will teach you how to
-do that.
+Services created by bundles are not defined in the main ``config/services.yaml``
+file used by the application but in the bundles themselves. This article
+explains how to create and load service files using the bundle directory
+structure.
+
+There are two different ways of doing it:
+
+#. :ref:`Load your services in the main bundle class `:
+ this is recommended for new bundles and for bundles following the
+ :ref:`recommended directory structure `;
+#. :ref:`Create an extension class to load the service configuration files `:
+ this was the traditional way of doing it, but nowadays it's only recommended for
+ bundles following the :ref:`legacy directory structure `.
+
+.. _bundle-load-services-bundle-class:
+
+Loading Services Directly in your Bundle Class
+----------------------------------------------
+
+In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
+class, you can define the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::loadExtension`
+method to load service definitions from configuration files::
+
+ // ...
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+ use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
+
+ class AcmeHelloBundle extends AbstractBundle
+ {
+ public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
+ {
+ // load an XML, PHP or YAML file
+ $container->import('../config/services.xml');
+
+ // you can also add or replace parameters and services
+ $container->parameters()
+ ->set('acme_hello.phrase', $config['phrase'])
+ ;
+
+ if ($config['scream']) {
+ $container->services()
+ ->get('acme_hello.printer')
+ ->class(ScreamingPrinter::class)
+ ;
+ }
+ }
+ }
+
+This method works similar to the ``Extension::load()`` method explained below,
+but it uses a new simpler API to define and import service configuration.
+
+.. note::
+
+ Contrary to the ``$configs`` parameter in ``Extension::load()``, the
+ ``$config`` parameter is already merged and processed by the
+ ``AbstractBundle``.
+
+.. note::
+
+ The ``loadExtension()`` is called only at compile time.
+
+.. _bundle-load-services-extension:
Creating an Extension Class
---------------------------
-In order to load service configuration, you have to create a Dependency
-Injection (DI) Extension for your bundle. This class has some conventions in order
-to be detected automatically. But you'll later see how you can change it to
-your own preferences. By default, the Extension has to comply with the
-following conventions:
+This is the traditional way of loading service definitions in bundles. For new
+bundles it's recommended to :ref:`load your services in the main bundle class `,
+but the traditional way of creating an extension class still works.
+
+A dependency injection extension is defined as a class that follows these
+conventions (later you'll learn how to skip them if needed):
* It has to live in the ``DependencyInjection`` namespace of the bundle;
+* It has to implement the :class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface`,
+ which is usually achieved by extending the
+ :class:`Symfony\\Component\\DependencyInjection\\Extension\\Extension` class;
+
* The name is equal to the bundle name with the ``Bundle`` suffix replaced by
- ``Extension`` (e.g. the Extension class of the AppBundle would be called
- ``AppExtension`` and the one for AcmeHelloBundle would be called
+ ``Extension`` (e.g. the extension class of the AcmeBundle would be called
+ ``AcmeExtension`` and the one for AcmeHelloBundle would be called
``AcmeHelloExtension``).
-The Extension class should implement the
-:class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface`,
-but usually you would simply extend the
-:class:`Symfony\\Component\\DependencyInjection\\Extension\\Extension` class::
+This is how the extension of an AcmeHelloBundle should look like::
- // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ // src/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
- use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Extension\Extension;
class AcmeHelloExtension extends Extension
{
- public function load(array $configs, ContainerBuilder $container)
+ public function load(array $configs, ContainerBuilder $container): void
{
// ... you'll load the files here later
}
@@ -56,26 +112,27 @@ method to return the instance of the extension::
// ...
use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass;
+ use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
class AcmeHelloBundle extends Bundle
{
- public function getContainerExtension()
+ public function getContainerExtension(): ?ExtensionInterface
{
return new UnconventionalExtensionClass();
}
}
-Since the new Extension class name doesn't follow the naming conventions, you
-should also override
+In addition, when the new Extension class name doesn't follow the naming
+conventions, you must also override the
:method:`Extension::getAlias() `
-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``).
Using the ``load()`` Method
----------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
In the ``load()`` method, all services and parameters related to this extension
will be loaded. This method doesn't get the actual container instance, but a
@@ -86,88 +143,53 @@ 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::
+``config/`` directory of your bundle, your ``load()`` method looks like::
- use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;
+ use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
// ...
- public function load(array $configs, ContainerBuilder $container)
+ public function load(array $configs, ContainerBuilder $container): void
{
$loader = new XmlFileLoader(
$container,
- new FileLocator(__DIR__.'/../Resources/config')
+ new FileLocator(__DIR__.'/../../config')
);
$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
-------------------------
-.. note::
+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::
- The ``addClassesToCompile()`` method was deprecated in Symfony 3.3, and will
- be removed in Symfony 4.0. If you want to use this method and be compatible
- with Symfony 4.0, check to see if the method exists before calling it.
-
-Symfony creates a big ``classes.php`` file in the cache directory to aggregate
-the contents of the PHP classes that are used in every request. This reduces the
-I/O operations and increases the application performance.
-
-.. 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;
-
- // ...
- public function load(array $configs, ContainerBuilder $container)
+ public function load(array $configs, ContainerBuilder $container): void
{
// ...
- // this method can't compile classes that contain PHP annotations
- $this->addClassesToCompile(array(
- UserManager::class,
- Slugger::class,
- // ...
- ));
+ $this->addAnnotatedClassesToCompile([
+ // you can define the fully qualified class names...
+ 'Acme\\BlogBundle\\Controller\\AuthorController',
+ // ... but glob patterns are also supported:
+ 'Acme\\BlogBundle\\Form\\**',
- // add here only classes that contain PHP annotations
- $this->addAnnotatedClassesToCompile(array(
- 'AppBundle\\Controller\\DefaultController',
// ...
- ));
+ ]);
}
.. note::
@@ -175,32 +197,11 @@ 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.
-.. caution::
+.. warning::
This technique can't be used when the classes to compile use the ``__DIR__``
or ``__FILE__`` constants, because their values will change when loading
diff --git a/bundles/index.rst b/bundles/index.rst
index 87641de5e23..58bcd13761e 100644
--- a/bundles/index.rst
+++ b/bundles/index.rst
@@ -4,11 +4,8 @@ Bundles
.. toctree::
:maxdepth: 2
- installation
- best_practices
- inheritance
override
- remove
- extension
+ best_practices
configuration
+ extension
prepend_extension
diff --git a/bundles/inheritance.rst b/bundles/inheritance.rst
deleted file mode 100644
index 784d022f926..00000000000
--- a/bundles/inheritance.rst
+++ /dev/null
@@ -1,107 +0,0 @@
-.. index::
- single: Bundle; Inheritance
-
-How to Use Bundle Inheritance to Override Parts of a Bundle
-===========================================================
-
-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
diff --git a/bundles/installation.rst b/bundles/installation.rst
deleted file mode 100644
index c37752b8388..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 37c38481472..f25bd785373 100644
--- a/bundles/override.rst
+++ b/bundles/override.rst
@@ -1,73 +1,90 @@
-.. index::
- single: Bundle; Inheritance
-
How to Override any Part of a Bundle
====================================
-This document is a quick reference for how to override different parts of
-third-party bundles.
+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.
+
+.. _override-templates:
Templates
---------
-For information on overriding templates, see
+Third-party bundle templates can be overridden in the
+``/templates/bundles//`` directory. The new templates
+must use the same name and path (relative to ``/templates/``) as
+the original templates.
+
+For example, to override the ``templates/registration/confirmed.html.twig``
+template from the AcmeUserBundle, create this template:
+``/templates/bundles/AcmeUserBundle/registration/confirmed.html.twig``
+
+.. warning::
+
+ If you add a template in a new location, you *may* need to clear your
+ cache (``php bin/console cache:clear``), even if you are in debug mode.
+
+Instead of overriding an entire template, you may just want to override one or
+more blocks. However, since you are overriding the template you want to extend
+from, you would end up in an infinite loop error. The solution is to use the
+special ``!`` prefix in the template name to tell Symfony that you want to
+extend from the original template, not from the overridden one:
+
+.. code-block:: twig
+
+ {# templates/bundles/AcmeUserBundle/registration/confirmed.html.twig #}
+ {# the special '!' prefix avoids errors when extending from an overridden template #}
+ {% extends "@!AcmeUser/registration/confirmed.html.twig" %}
-* :doc:`/templating/overriding`.
-* :doc:`/bundles/inheritance`
+ {% block some_block %}
+ ...
+ {% endblock %}
+
+.. _templating-overriding-core-templates:
+
+.. tip::
+
+ Symfony internals use some bundles too, so you can apply the same technique
+ to override the core Symfony templates. For example, you can
+ :doc:`customize error pages ` overriding TwigBundle
+ templates.
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
+all. Instead of importing a third-party bundle's routing, copy
that routing file into your application, modify it, and import it instead.
Controllers
-----------
-Assuming the third-party bundle involved uses non-service controllers (which
-is almost always the case), you can easily override controllers via bundle
-inheritance. For more information, see :doc:`/bundles/inheritance`.
If the controller is a service, see the next section on how to override it.
+Otherwise, define a new route + controller with the same path associated to the
+controller you want to override (and make sure that the new route is loaded
+before the bundle one).
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``::
-
- // src/Acme/DemoBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php
- namespace Acme\DemoBundle\DependencyInjection\Compiler;
+If you want to modify the services created by a bundle, you can use
+:doc:`service decoration `.
- use Acme\DemoBundle\YourService;
- use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
- use Symfony\Component\DependencyInjection\ContainerBuilder;
-
- class OverrideServiceCompilerPass implements CompilerPassInterface
- {
- public function process(ContainerBuilder $container)
- {
- $definition = $container->getDefinition('original-service-id');
- $definition->setClass(YourService::class);
- }
- }
-
-For more information on compiler passes, see :doc:`/service_container/compiler_passes`.
+If you want to do more advanced manipulations, like removing services created by
+other bundles, you must work with :doc:`service definitions `
+inside a :doc:`compiler pass `.
Entities & Entity Mapping
-------------------------
-Due to the way Doctrine works, it is not possible to override entity mapping
-of a bundle. However, if a bundle provides a mapped superclass (such as the
-``User`` entity in the FOSUserBundle) one can override attributes and
-associations. Learn more about this feature and its limitations in
-`the Doctrine documentation`_.
+Overriding entity mapping is only possible if a bundle provides a mapped
+superclass (such as the ``User`` entity in the FOSUserBundle). It's possible to
+override attributes and associations in this way. Learn more about this feature
+and its limitations in `the Doctrine documentation`_.
Forms
-----
@@ -93,7 +110,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:
@@ -106,13 +123,13 @@ to a new validation group:
.. code-block:: xml
-
+
-
+ https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"
+ >
@@ -140,17 +157,12 @@ instead of the original ones.
Translations
------------
-Translations are not related to bundles, but to domains. That means that you
-can override the translations from any translation file, as long as it is in
-:ref:`the correct domain `.
-
-.. caution::
+Translations are not related to bundles, but to translation domains.
+For this reason, you can override any bundle translation file from the main
+``translations/`` directory, as long as the new file uses the same domain.
- Translation files are not aware of :doc:`bundle inheritance `.
- If you want to override translations from the parent bundle or another bundle,
- make sure that the bundle containing *your* translations is loaded after any
- bundle whose translations you're overriding. This is done in ``AppKernel``.
+For example, to override the translations defined in the
+``translations/AcmeUserBundle.es.yaml`` file of the AcmeUserBundle,
+create a ``/translations/AcmeUserBundle.es.yaml`` file.
- Finally, translations located in ``app/Resources/translations`` will override
- all the other translations since those files are always loaded last.
-.. _`the Doctrine documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#overrides
+.. _`the Doctrine documentation`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/inheritance-mapping.html#overrides
diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst
index 28610a27154..e4099d9f81a 100644
--- a/bundles/prepend_extension.rst
+++ b/bundles/prepend_extension.rst
@@ -1,22 +1,17 @@
-.. index::
- single: Configuration; Semantic
- single: Bundle; Extension configuration
-
How to Simplify Configuration of Multiple Bundles
=================================================
When building reusable and extensible applications, developers are often
faced with a choice: either create a single large bundle or multiple smaller
bundles. Creating a single bundle has the drawback that it's impossible for
-users to choose to remove functionality they are not using. Creating multiple
+users to remove unused functionality. Creating multiple
bundles has the drawback that configuration becomes more tedious and settings
often need to be repeated for various bundles.
-It is possible to remove the disadvantage of the multiple bundle approach
-by enabling a single Extension to prepend the settings for any bundle.
-It can use the settings defined in the ``app/config/config.yml``
-to prepend settings just as if they had been written explicitly by
-the user in the application configuration.
+It is possible to remove the disadvantage of the multiple bundle approach by
+enabling a single Extension to prepend the settings for any bundle. It can use
+the settings defined in the ``config/*`` files to prepend settings just as if
+they had been written explicitly by the user in the application configuration.
For example, this could be used to configure the entity manager name to use in
multiple bundles. Or it can be used to enable an optional feature that depends
@@ -28,15 +23,15 @@ To give an Extension the power to do this, it needs to implement
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
namespace Acme\HelloBundle\DependencyInjection;
- use Symfony\Component\HttpKernel\DependencyInjection\Extension;
- use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
+ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
class AcmeHelloExtension extends Extension implements PrependExtensionInterface
{
// ...
- public function prepend(ContainerBuilder $container)
+ public function prepend(ContainerBuilder $container): void
{
// ...
}
@@ -50,60 +45,60 @@ prepend settings to a bundle extension developers can use the
:method:`Symfony\\Component\\DependencyInjection\\ContainerBuilder::prependExtensionConfig`
method on the :class:`Symfony\\Component\\DependencyInjection\\ContainerBuilder`
instance. As this method only prepends settings, any other settings done explicitly
-inside the ``app/config/config.yml`` would override these prepended settings.
+inside the ``config/*`` files would override these prepended settings.
The following example illustrates how to prepend
a configuration setting in multiple bundles as well as disable a flag in multiple bundles
in case a specific other bundle is not registered::
// src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
- public function prepend(ContainerBuilder $container)
+ public function prepend(ContainerBuilder $container): void
{
// get all bundles
$bundles = $container->getParameter('kernel.bundles');
// determine if AcmeGoodbyeBundle is registered
if (!isset($bundles['AcmeGoodbyeBundle'])) {
// disable AcmeGoodbyeBundle in bundles
- $config = array('use_acme_goodbye' => false);
+ $config = ['use_acme_goodbye' => false];
foreach ($container->getExtensions() as $name => $extension) {
- switch ($name) {
- case 'acme_something':
- case 'acme_other':
- // set use_acme_goodbye to false in the config of
- // acme_something and acme_other
- //
- // note that if the user manually configured
- // use_acme_goodbye to true in app/config/config.yml
- // then the setting would in the end be true and not false
- $container->prependExtensionConfig($name, $config);
- break;
- }
+ match ($name) {
+ // set use_acme_goodbye to false in the config of
+ // acme_something and acme_other
+ //
+ // note that if the user manually configured
+ // use_acme_goodbye to true in config/services.yaml
+ // then the setting would in the end be true and not false
+ 'acme_something', 'acme_other' => $container->prependExtensionConfig($name, $config),
+ default => null
+ };
}
}
- // process the configuration of AcmeHelloExtension
+ // get the configuration of AcmeHelloExtension (it's a list of configuration)
$configs = $container->getExtensionConfig($this->getAlias());
- // use the Configuration class to generate a config array with
- // the settings "acme_hello"
- $config = $this->processConfiguration(new Configuration(), $configs);
-
- // check if entity_manager_name is set in the "acme_hello" configuration
- if (isset($config['entity_manager_name'])) {
- // prepend the acme_something settings with the entity_manager_name
- $config = array('entity_manager_name' => $config['entity_manager_name']);
- $container->prependExtensionConfig('acme_something', $config);
+
+ // iterate in reverse to preserve the original order after prepending the config
+ foreach (array_reverse($configs) as $config) {
+ // check if entity_manager_name is set in the "acme_hello" configuration
+ if (isset($config['entity_manager_name'])) {
+ // prepend the acme_something settings with the entity_manager_name
+ $container->prependExtensionConfig('acme_something', [
+ 'entity_manager_name' => $config['entity_manager_name'],
+ ]);
+ }
}
}
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,35 +110,110 @@ The above would be the equivalent of writing the following into the
.. code-block:: xml
-
+
-
+ https://symfony.com/schema/dic/services/services-1.0.xsd
+ http://example.org/schema/dic/acme_something
+ https://example.org/schema/dic/acme_something/acme_something-1.0.xsd
+ http://example.org/schema/dic/acme_other
+ https://example.org/schema/dic/acme_something/acme_other-1.0.xsd"
+ >
+
non_default
-
+
+
+
.. code-block:: php
- // app/config/config.php
- $container->loadFromExtension('acme_something', array(
+ // config/packages/acme_something.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ return static function (ContainerConfigurator $container): void {
+ $container->extension('acme_something', [
+ // ...
+ 'use_acme_goodbye' => false,
+ 'entity_manager_name' => 'non_default',
+ ]);
+ $container->extension('acme_other', [
+ // ...
+ 'use_acme_goodbye' => false,
+ ]);
+ };
+
+Prepending Extension in the Bundle Class
+----------------------------------------
+
+You can also prepend extension configuration directly in your
+Bundle class if you extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
+class and define the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::prependExtension`
+method::
+
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+ use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
+
+ class FooBundle extends AbstractBundle
+ {
+ public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
+ {
+ // prepend
+ $containerBuilder->prependExtensionConfig('framework', [
+ 'cache' => ['prefix_seed' => 'foo/bar'],
+ ]);
+
+ // prepend config from a file
+ $containerConfigurator->import('../config/packages/cache.php');
+ }
+ }
+
+.. note::
+
+ The ``prependExtension()`` method, like ``prepend()``, is called only at compile time.
+
+.. versionadded:: 7.1
+
+ Starting from Symfony 7.1, calling the :method:`Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator::import`
+ method inside ``prependExtension()`` will prepend the given configuration.
+ In previous Symfony versions, this method appended the configuration.
+
+Alternatively, you can use the ``prepend`` parameter of the
+:method:`Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator::extension`
+method::
+
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+ use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
+
+ class FooBundle extends AbstractBundle
+ {
+ public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
+ {
// ...
- 'use_acme_goodbye' => false,
- 'entity_manager_name' => 'non_default',
- ));
- $container->loadFromExtension('acme_other', array(
+
+ $containerConfigurator->extension('framework', [
+ 'cache' => ['prefix_seed' => 'foo/bar'],
+ ], prepend: true);
+
// ...
- 'use_acme_goodbye' => false,
- ));
+ }
+ }
+
+.. versionadded:: 7.1
+
+ The ``prepend`` parameter of the
+ :method:`Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator::extension`
+ method was added in Symfony 7.1.
More than one Bundle using PrependExtensionInterface
----------------------------------------------------
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/cache.rst b/cache.rst
new file mode 100644
index 00000000000..83bb5b4cedc
--- /dev/null
+++ b/cache.rst
@@ -0,0 +1,980 @@
+Cache
+=====
+
+Using a cache is a great way of making your application run quicker. The Symfony cache
+component ships with many adapters to different storages. Every adapter is
+developed for high performance.
+
+The following example shows a typical usage of the cache::
+
+ use Symfony\Contracts\Cache\ItemInterface;
+
+ // The callable will only be executed on a cache miss.
+ $value = $pool->get('my_cache_key', function (ItemInterface $item): string {
+ $item->expiresAfter(3600);
+
+ // ... do some HTTP request or heavy computations
+ $computedValue = 'foobar';
+
+ return $computedValue;
+ });
+
+ echo $value; // 'foobar'
+
+ // ... and to remove the cache key
+ $pool->delete('my_cache_key');
+
+Symfony supports Cache Contracts and PSR-6/16 interfaces.
+You can read more about these at the :doc:`component documentation `.
+
+.. _cache-configuration-with-frameworkbundle:
+
+Configuring Cache with FrameworkBundle
+--------------------------------------
+
+When configuring the cache component there are a few concepts you should know
+of:
+
+**Pool**
+ This is a service that you will interact with. Each pool will always have
+ its own namespace and cache items. There is never a conflict between pools.
+**Adapter**
+ An adapter is a *template* that you use to create pools.
+**Provider**
+ A provider is a service that some adapters use to connect to the storage.
+ Redis and Memcached are examples of such adapters. If a DSN is used as the
+ provider then a service is automatically created.
+
+.. _cache-app-system:
+
+There are two pools that are always enabled by default. They are ``cache.app`` and
+``cache.system``. The system cache is used for things like annotations, serializer,
+and validation. The ``cache.app`` can be used in your code. You can configure which
+adapter (template) they use by using the ``app`` and ``system`` key like:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ app: cache.adapter.filesystem
+ system: cache.adapter.system
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ ->app('cache.adapter.filesystem')
+ ->system('cache.adapter.system')
+ ;
+ };
+
+.. tip::
+
+ While it is possible to reconfigure the ``system`` cache, it's recommended
+ to keep the default configuration applied to it by Symfony.
+
+The Cache component comes with a series of adapters pre-configured:
+
+* :doc:`cache.adapter.apcu `
+* :doc:`cache.adapter.array `
+* :doc:`cache.adapter.doctrine_dbal `
+* :doc:`cache.adapter.filesystem `
+* :doc:`cache.adapter.memcached `
+* :doc:`cache.adapter.pdo `
+* :doc:`cache.adapter.psr6 `
+* :doc:`cache.adapter.redis `
+* :ref:`cache.adapter.redis_tag_aware ` (Redis adapter optimized to work with tags)
+
+.. note::
+
+ There's also a special ``cache.adapter.system`` adapter. It's recommended to
+ use it for the :ref:`system cache `. This adapter uses some
+ logic to dynamically select the best possible storage based on your system
+ (either PHP files or APCu).
+
+Some of these adapters could be configured via shortcuts.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ directory: '%kernel.cache_dir%/pools' # Only used with cache.adapter.filesystem
+
+ default_doctrine_dbal_provider: 'doctrine.dbal.default_connection'
+ default_psr6_provider: 'app.my_psr6_service'
+ default_redis_provider: 'redis://localhost'
+ default_memcached_provider: 'memcached://localhost'
+ default_pdo_provider: 'pgsql:host=localhost'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ // Only used with cache.adapter.filesystem
+ ->directory('%kernel.cache_dir%/pools')
+
+ ->defaultDoctrineDbalProvider('doctrine.dbal.default_connection')
+ ->defaultPsr6Provider('app.my_psr6_service')
+ ->defaultRedisProvider('redis://localhost')
+ ->defaultMemcachedProvider('memcached://localhost')
+ ->defaultPdoProvider('pgsql:host=localhost')
+ ;
+ };
+
+.. versionadded:: 7.1
+
+ Using a DSN as the provider for the PDO adapter was introduced in Symfony 7.1.
+
+.. _cache-create-pools:
+
+Creating Custom (Namespaced) Pools
+----------------------------------
+
+You can also create more customized pools:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ default_memcached_provider: 'memcached://localhost'
+
+ pools:
+ # creates a "custom_thing.cache" service
+ # autowireable via "CacheInterface $customThingCache"
+ # uses the "app" cache configuration
+ custom_thing.cache:
+ adapter: cache.app
+
+ # creates a "my_cache_pool" service
+ # autowireable via "CacheInterface $myCachePool"
+ my_cache_pool:
+ adapter: cache.adapter.filesystem
+
+ # uses the default_memcached_provider from above
+ acme.cache:
+ adapter: cache.adapter.memcached
+
+ # control adapter's configuration
+ foobar.cache:
+ adapter: cache.adapter.memcached
+ provider: 'memcached://user:password@example.com'
+
+ # uses the "foobar.cache" pool as its backend but controls
+ # the lifetime and (like all pools) has a separate cache namespace
+ short_cache:
+ adapter: foobar.cache
+ default_lifetime: 60
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $cache = $framework->cache();
+ $cache->defaultMemcachedProvider('memcached://localhost');
+
+ // creates a "custom_thing.cache" service
+ // autowireable via "CacheInterface $customThingCache"
+ // uses the "app" cache configuration
+ $cache->pool('custom_thing.cache')
+ ->adapters(['cache.app']);
+
+ // creates a "my_cache_pool" service
+ // autowireable via "CacheInterface $myCachePool"
+ $cache->pool('my_cache_pool')
+ ->adapters(['cache.adapter.filesystem']);
+
+ // uses the default_memcached_provider from above
+ $cache->pool('acme.cache')
+ ->adapters(['cache.adapter.memcached']);
+
+ // control adapter's configuration
+ $cache->pool('foobar.cache')
+ ->adapters(['cache.adapter.memcached'])
+ ->provider('memcached://user:password@example.com');
+
+ $cache->pool('short_cache')
+ ->adapters(['foobar.cache'])
+ ->defaultLifetime(60);
+ };
+
+Each pool manages a set of independent cache keys: keys from different pools
+*never* collide, even if they share the same backend. This is achieved by prefixing
+keys with a namespace that's generated by hashing the name of the pool, the name
+of the cache adapter class and a :ref:`configurable seed `
+that defaults to the project directory and compiled container class.
+
+Each custom pool becomes a service whose service ID is the name of the pool
+(e.g. ``custom_thing.cache``). An autowiring alias is also created for each pool
+using the camel case version of its name - e.g. ``custom_thing.cache`` can be
+injected automatically by naming the argument ``$customThingCache`` and type-hinting it
+with either :class:`Symfony\\Contracts\\Cache\\CacheInterface` or
+``Psr\Cache\CacheItemPoolInterface``::
+
+ use Symfony\Contracts\Cache\CacheInterface;
+ // ...
+
+ // from a controller method
+ public function listProducts(CacheInterface $customThingCache): Response
+ {
+ // ...
+ }
+
+ // in a service
+ public function __construct(private CacheInterface $customThingCache)
+ {
+ // ...
+ }
+
+.. tip::
+
+ If you need the namespace to be interoperable with a third-party app,
+ you can take control over auto-generation by setting the ``namespace``
+ attribute of the ``cache.pool`` service tag. For example, you can
+ override the service definition of the adapter:
+
+ .. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/services.yaml
+ services:
+ # ...
+
+ app.cache.adapter.redis:
+ parent: 'cache.adapter.redis'
+ tags:
+ - { name: 'cache.pool', namespace: 'my_custom_namespace' }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ return function(ContainerConfigurator $container): void {
+ $container->services()
+ // ...
+
+ ->set('app.cache.adapter.redis')
+ ->parent('cache.adapter.redis')
+ ->tag('cache.pool', ['namespace' => 'my_custom_namespace'])
+ ;
+ };
+
+Custom Provider Options
+-----------------------
+
+Some providers have specific options that can be configured. The
+:doc:`RedisAdapter ` allows you to
+create providers with the options ``timeout``, ``retry_interval``. etc. To use these
+options with non-default values you need to create your own ``\Redis`` provider
+and use that when configuring the pool.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ pools:
+ cache.my_redis:
+ adapter: cache.adapter.redis
+ provider: app.my_custom_redis_provider
+
+ services:
+ app.my_custom_redis_provider:
+ class: \Redis
+ factory: ['Symfony\Component\Cache\Adapter\RedisAdapter', 'createConnection']
+ arguments:
+ - 'redis://localhost'
+ - { retry_interval: 2, timeout: 10 }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ redis://localhost
+
+ 2
+ 10
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ use Symfony\Component\Cache\Adapter\RedisAdapter;
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (ContainerBuilder $container, FrameworkConfig $framework): void {
+ $framework->cache()
+ ->pool('cache.my_redis')
+ ->adapters(['cache.adapter.redis'])
+ ->provider('app.my_custom_redis_provider');
+
+ $container->register('app.my_custom_redis_provider', \Redis::class)
+ ->setFactory([RedisAdapter::class, 'createConnection'])
+ ->addArgument('redis://localhost')
+ ->addArgument([
+ 'retry_interval' => 2,
+ 'timeout' => 10
+ ])
+ ;
+ };
+
+Creating a Cache Chain
+----------------------
+
+Different cache adapters have different strengths and weaknesses. Some might be
+really quick but optimized to store small items and some may be able to contain
+a lot of data but are quite slow. To get the best of both worlds you may use a
+chain of adapters.
+
+A cache chain combines several cache pools into a single one. When storing an
+item in a cache chain, Symfony stores it in all pools sequentially. When
+retrieving an item, Symfony tries to get it from the first pool. If it's not
+found, it tries the next pools until the item is found or an exception is thrown.
+Because of this behavior, it's recommended to define the adapters in the chain
+in order from fastest to slowest.
+
+If an error happens when storing an item in a pool, Symfony stores it in the
+other pools and no exception is thrown. Later, when the item is retrieved,
+Symfony stores the item automatically in all the missing pools.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ pools:
+ my_cache_pool:
+ default_lifetime: 31536000 # One year
+ adapters:
+ - cache.adapter.array
+ - cache.adapter.apcu
+ - {name: cache.adapter.redis, provider: 'redis://user:password@example.com'}
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ ->pool('my_cache_pool')
+ ->defaultLifetime(31536000) // One year
+ ->adapters([
+ 'cache.adapter.array',
+ 'cache.adapter.apcu',
+ ['name' => 'cache.adapter.redis', 'provider' => 'redis://user:password@example.com'],
+ ])
+ ;
+ };
+
+Using Cache Tags
+----------------
+
+In applications with many cache keys it could be useful to organize the data stored
+to be able to invalidate the cache more efficiently. One way to achieve that is to
+use cache tags. One or more tags could be added to the cache item. All items with
+the same tag could be invalidated with one function call::
+
+ use Symfony\Contracts\Cache\ItemInterface;
+ use Symfony\Contracts\Cache\TagAwareCacheInterface;
+
+ class SomeClass
+ {
+ // using autowiring to inject the cache pool
+ public function __construct(
+ private TagAwareCacheInterface $myCachePool,
+ ) {
+ }
+
+ public function someMethod(): void
+ {
+ $value0 = $this->myCachePool->get('item_0', function (ItemInterface $item): string {
+ $item->tag(['foo', 'bar']);
+
+ return 'debug';
+ });
+
+ $value1 = $this->myCachePool->get('item_1', function (ItemInterface $item): string {
+ $item->tag('foo');
+
+ return 'debug';
+ });
+
+ // Remove all cache keys tagged with "bar"
+ $this->myCachePool->invalidateTags(['bar']);
+ }
+ }
+
+The cache adapter needs to implement :class:`Symfony\\Contracts\\Cache\\TagAwareCacheInterface`
+to enable this feature. This could be added by using the following configuration.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ pools:
+ my_cache_pool:
+ adapter: cache.adapter.redis_tag_aware
+ tags: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ ->pool('my_cache_pool')
+ ->tags(true)
+ ->adapters(['cache.adapter.redis_tag_aware'])
+ ;
+ };
+
+Tags are stored in the same pool by default. This is good in most scenarios. But
+sometimes it might be better to store the tags in a different pool. That could be
+achieved by specifying the adapter.
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+ framework:
+ cache:
+ pools:
+ my_cache_pool:
+ adapter: cache.adapter.redis
+ tags: tag_pool
+ tag_pool:
+ adapter: cache.adapter.apcu
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ ->pool('my_cache_pool')
+ ->tags('tag_pool')
+ ->adapters(['cache.adapter.redis'])
+ ;
+
+ $framework->cache()
+ ->pool('tag_pool')
+ ->adapters(['cache.adapter.apcu'])
+ ;
+ };
+
+.. note::
+
+ The interface :class:`Symfony\\Contracts\\Cache\\TagAwareCacheInterface` is
+ autowired to the ``cache.app`` service.
+
+Clearing the Cache
+------------------
+
+To clear the cache you can use the ``bin/console cache:pool:clear [pool]`` command.
+That will remove all the entries from your storage and you will have to recalculate
+all the values. You can also group your pools into "cache clearers". There are 3 cache
+clearers by default:
+
+* ``cache.global_clearer``
+* ``cache.system_clearer``
+* ``cache.app_clearer``
+
+The global clearer clears all the cache items in every pool. The system cache clearer
+is used in the ``bin/console cache:clear`` command. The app clearer is the default
+clearer.
+
+To see all available cache pools:
+
+.. code-block:: terminal
+
+ $ php bin/console cache:pool:list
+
+Clear one pool:
+
+.. code-block:: terminal
+
+ $ php bin/console cache:pool:clear my_cache_pool
+
+Clear all custom pools:
+
+.. code-block:: terminal
+
+ $ php bin/console cache:pool:clear cache.app_clearer
+
+Clear all cache pools:
+
+.. code-block:: terminal
+
+ $ php bin/console cache:pool:clear --all
+
+Clear all cache pools except some:
+
+.. code-block:: terminal
+
+ $ php bin/console cache:pool:clear --all --exclude=my_cache_pool --exclude=another_cache_pool
+
+Clear all caches everywhere:
+
+.. code-block:: terminal
+
+ $ php bin/console cache:pool:clear cache.global_clearer
+
+Clear cache by tag(s):
+
+.. code-block:: terminal
+
+ # invalidate tag1 from all taggable pools
+ $ php bin/console cache:pool:invalidate-tags tag1
+
+ # invalidate tag1 & tag2 from all taggable pools
+ $ php bin/console cache:pool:invalidate-tags tag1 tag2
+
+ # invalidate tag1 & tag2 from cache.app pool
+ $ php bin/console cache:pool:invalidate-tags tag1 tag2 --pool=cache.app
+
+ # invalidate tag1 & tag2 from cache1 & cache2 pools
+ $ php bin/console cache:pool:invalidate-tags tag1 tag2 -p cache1 -p cache2
+
+Encrypting the Cache
+--------------------
+
+To encrypt the cache using ``libsodium``, you can use the
+:class:`Symfony\\Component\\Cache\\Marshaller\\SodiumMarshaller`.
+
+First, you need to generate a secure key and add it to your :doc:`secret
+store ` as ``CACHE_DECRYPTION_KEY``:
+
+.. code-block:: terminal
+
+ $ php -r 'echo base64_encode(sodium_crypto_box_keypair());'
+
+Then, register the ``SodiumMarshaller`` service using this key:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/cache.yaml
+
+ # ...
+ services:
+ Symfony\Component\Cache\Marshaller\SodiumMarshaller:
+ decorates: cache.default_marshaller
+ arguments:
+ - ['%env(base64:CACHE_DECRYPTION_KEY)%']
+ # use multiple keys in order to rotate them
+ #- ['%env(base64:CACHE_DECRYPTION_KEY)%', '%env(base64:OLD_CACHE_DECRYPTION_KEY)%']
+ - '@.inner'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ env(base64:CACHE_DECRYPTION_KEY)
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Component\Cache\Marshaller\SodiumMarshaller;
+ use Symfony\Component\DependencyInjection\ChildDefinition;
+ use Symfony\Component\DependencyInjection\Reference;
+
+ // ...
+ $container->setDefinition(SodiumMarshaller::class, new ChildDefinition('cache.default_marshaller'))
+ ->addArgument(['env(base64:CACHE_DECRYPTION_KEY)'])
+ // use multiple keys in order to rotate them
+ //->addArgument(['env(base64:CACHE_DECRYPTION_KEY)', 'env(base64:OLD_CACHE_DECRYPTION_KEY)'])
+ ->addArgument(new Reference('.inner'));
+
+.. danger::
+
+ This will encrypt the values of the cache items, but not the cache keys. Be
+ careful not to leak sensitive data in the keys.
+
+When configuring multiple keys, the first key will be used for reading and
+writing, and the additional key(s) will only be used for reading. Once all
+cache items encrypted with the old key have expired, you can completely remove
+``OLD_CACHE_DECRYPTION_KEY``.
+
+Computing Cache Values Asynchronously
+-------------------------------------
+
+The Cache component uses the `probabilistic early expiration`_ algorithm to
+protect against the :ref:`cache stampede ` problem.
+This means that some cache items are elected for early-expiration while they are
+still fresh.
+
+By default, expired cache items are computed synchronously. However, you can
+compute them asynchronously by delegating the value computation to a background
+worker using the :doc:`Messenger component `. In this case,
+when an item is queried, its cached value is immediately returned and a
+:class:`Symfony\\Component\\Cache\\Messenger\\EarlyExpirationMessage` is
+dispatched through a Messenger bus.
+
+When this message is handled by a message consumer, the refreshed cache value is
+computed asynchronously. The next time the item is queried, the refreshed value
+will be fresh and returned.
+
+First, create a service that will compute the item's value::
+
+ // src/Cache/CacheComputation.php
+ namespace App\Cache;
+
+ use Symfony\Contracts\Cache\ItemInterface;
+
+ class CacheComputation
+ {
+ public function compute(ItemInterface $item): string
+ {
+ $item->expiresAfter(5);
+
+ // this is just a random example; here you must do your own calculation
+ return sprintf('#%06X', mt_rand(0, 0xFFFFFF));
+ }
+ }
+
+This cache value will be requested from a controller, another service, etc.
+In the following example, the value is requested from a controller::
+
+ // src/Controller/CacheController.php
+ namespace App\Controller;
+
+ use App\Cache\CacheComputation;
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\Routing\Attribute\Route;
+ use Symfony\Contracts\Cache\CacheInterface;
+ use Symfony\Contracts\Cache\ItemInterface;
+
+ class CacheController extends AbstractController
+ {
+ #[Route('/cache', name: 'cache')]
+ public function index(CacheInterface $asyncCache): Response
+ {
+ // pass to the cache the service method that refreshes the item
+ $cachedValue = $asyncCache->get('my_value', [CacheComputation::class, 'compute'])
+
+ // ...
+ }
+ }
+
+Finally, configure a new cache pool (e.g. called ``async.cache``) that will use
+a message bus to compute values in a worker:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ cache:
+ pools:
+ async.cache:
+ early_expiration_message_bus: messenger.default_bus
+
+ messenger:
+ transports:
+ async_bus: '%env(MESSENGER_TRANSPORT_DSN)%'
+ routing:
+ 'Symfony\Component\Cache\Messenger\EarlyExpirationMessage': async_bus
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ %env(MESSENGER_TRANSPORT_DSN)%
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/framework/framework.php
+ use function Symfony\Component\DependencyInjection\Loader\Configurator\env;
+ use Symfony\Component\Cache\Messenger\EarlyExpirationMessage;
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ ->pool('async.cache')
+ ->earlyExpirationMessageBus('messenger.default_bus');
+
+ $framework->messenger()
+ ->transport('async_bus')
+ ->dsn(env('MESSENGER_TRANSPORT_DSN'))
+ ->routing(EarlyExpirationMessage::class)
+ ->senders(['async_bus']);
+ };
+
+You can now start the consumer:
+
+.. code-block:: terminal
+
+ $ php bin/console messenger:consume async_bus
+
+That's it! Now, whenever an item is queried from this cache pool, its cached
+value will be returned immediately. If it is elected for early-expiration, a
+message will be sent through to bus to schedule a background computation to refresh
+the value.
+
+.. _`probabilistic early expiration`: https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
diff --git a/changelog.rst b/changelog.rst
deleted file mode 100644
index f91895f535c..00000000000
--- a/changelog.rst
+++ /dev/null
@@ -1,2731 +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