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
new file mode 100644
index 00000000000..f9366facfb0
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+indent_style = space
+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/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 00000000000..acb0770920e
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,5 @@
+Contributing
+------------
+
+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).
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
new file mode 100644
index 00000000000..b69047f69a1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/_build/vendor
+/_build/output
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.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/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/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/redirection_map b/_build/redirection_map
new file mode 100644
index 00000000000..ee14c191025
--- /dev/null
+++ b/_build/redirection_map
@@ -0,0 +1,576 @@
+/book/index /index
+/cookbook/index /index
+/book/stable_api /contributing/code/bc
+/book/internals /reference/events
+/configuration/apache_router /routing
+/cookbook/console/sending_emails /cookbook/console/request_context
+/cookbook/deployment-tools /cookbook/deployment/tools
+/cookbook/doctrine/migrations /bundles/DoctrineFixturesBundle/index
+/cookbook/doctrine/doctrine_fixtures /bundles/DoctrineFixturesBundle/index
+/cookbook/doctrine/mongodb /bundles/DoctrineMongoDBBundle/index
+/cookbook/form/dynamic_form_generation /cookbook/form/dynamic_form_modification
+/cookbook/form/simple_signup_form_with_mongodb /bundles/DoctrineMongoDBBundle/form
+/cookbook/email /email
+/cookbook/gmail /cookbook/email/gmail
+/cookbook/console /components/console
+/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
+/cookbook/service_container/tags /service_container/tags
+/reference/configuration/mongodb /bundles/DoctrineMongoDBBundle/config
+/reference/YAML /components/yaml
+/cookbook/console/generating_urls /cookbook/console/sending_emails
+/cmf/reference/configuration/block /cmf/bundles/block/configuration
+/cmf/reference/configuration/content /cmf/bundles/content/configuration
+/cmf/reference/configuration/core /cmf/bundles/core/configuration
+/cmf/reference/configuration/create /cmf/bundles/create/configuration
+/cmf/reference/configuration/media /cmf/bundles/media/configuration
+/cmf/reference/configuration/menu /cmf/bundles/menu/configuration
+/cmf/reference/configuration/phpcr_odm /cmf/bundles/phpcr_odm/configuration
+/cmf/reference/configuration/routing /cmf/bundles/routing/configuration
+/cmf/reference/configuration/search /cmf/bundles/search/configuration
+/cmf/reference/configuration/seo /cmf/bundles/seo/configuration
+/cmf/reference/configuration/simple_cms /cmf/bundles/simple_cms/configuration
+/cmf/reference/configuration/tree_browser /cmf/bundles/tree_browser/configuration
+/cmf/cookbook/exposing_content_via_rest /cmf/bundles/content/exposing_content_via_rest
+/cmf/cookbook/creating_a_cms/auto-routing /cmf/tutorial/auto-routing
+/cmf/cookbook/creating_a_cms/conclusion /cmf/tutorial/conclusion
+/cmf/cookbook/creating_a_cms/content-to-controllers /cmf/tutorial/content-to-controllers
+/cmf/cookbook/creating_a_cms/getting-started /cmf/tutorial/getting-started
+/cmf/cookbook/creating_a_cms/index /cmf/tutorial/index
+/cmf/cookbook/creating_a_cms/introduction /cmf/tutorial/introduction
+/cmf/cookbook/creating_a_cms/make-homepage /cmf/tutorial/make-homepage
+/cmf/cookbook/creating_a_cms/sonata-admin /cmf/tutorial/sonata-admin
+/cmf/cookbook/creating_a_cms/the-frontend /cmf/tutorial/the-frontend
+/cookbook/upgrading /cookbook/upgrade/index
+/cookbook/security/voters_data_permission /cookbook/security/voters
+/cookbook/configuration/pdo_session_storage /cookbook/doctrine/pdo_session_storage
+/cookbook/configuration/mongodb_session_storage /cookbook/doctrine/mongodb_session_storage
+/cookbook/service_container/event_listener /event_dispatcher
+/create_framework/http-foundation /create_framework/http_foundation
+/create_framework/front-controller /create_framework/front_controller
+/create_framework/http-kernel-controller-resolver /create_framework/http_kernel_controller_resolver
+/create_framework/separation-of-concerns /create_framework/separation_of_concerns
+/create_framework/unit-testing /create_framework/unit_testing
+/create_framework/event-dispatcher /create_framework/event_dispatcher
+/create_framework/http-kernel-httpkernelinterface /create_framework/http_kernel_httpkernelinterface
+/create_framework/http-kernel-httpkernel-class /create_framework/http_kernel_httpkernel_class
+/create_framework/dependency-injection /create_framework/dependency_injection
+/cookbook/doctrine/file_uploads /cookbook/controller/upload_file
+/book/installation /setup
+/book/page_creation /page_creation
+/book/controller /controller
+/book/routing /routing
+/book/templating /templating
+/book/bundles /bundles
+/book/doctrine /doctrine
+/book/testing /testing
+/book/validation /validation
+/book/forms /forms
+/book/security /security
+/book/http_cache /http_cache
+/book/translation /translation
+/book/service_container /service_container
+/book/http_fundamentals /introduction/http_fundamentals
+/book/from_flat_php_to_symfony2 /introduction/from_flat_php_to_symfony2
+/book/configuration /configuration
+/book/propel /propel/propel
+/book/performance /performance
+/bundles/installation /bundles
+/cookbook/assetic/apply_to_option /frontend/assetic/apply_to_option
+/cookbook/assetic/asset_management /frontend/assetic/asset_management
+/cookbook/assetic/index /frontend/assetic/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
+/cookbook/bundles/override /bundles/override
+/cookbook/bundles/prepend_extension /bundles/prepend_extension
+/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
+/cookbook/configuration/apache_router /routing
+/cookbook/configuration/configuration_organization /configuration/configuration_organization
+/cookbook/configuration/environments /configuration/environments
+/cookbook/configuration/external_parameters /configuration/external_parameters
+/cookbook/configuration/front_controllers_and_kernel /configuration/front_controllers_and_kernel
+/cookbook/configuration/micro-kernel-trait /configuration/micro_kernel_trait
+/cookbook/configuration/index /configuration
+/cookbook/configuration/override_dir_structure /configuration/override_dir_structure
+/cookbook/configuration/using_parameters_in_dic /configuration/using_parameters_in_dic
+/cookbook/configuration/web_server_configuration /setup/web_server_configuration
+/cookbook/console/command_in_controller /console/command_in_controller
+/cookbook/console/commands_as_services /console/commands_as_services
+/cookbook/console/console_command /console
+/cookbook/console/index /console
+/cookbook/console/logging /console
+/cookbook/console/request_context /console/request_context
+/cookbook/console/style /console/style
+/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/tools /deployment/tools
+/cookbook/doctrine/common_extensions /doctrine/common_extensions
+/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
+/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
+/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
+/cookbook/email/index /email
+/cookbook/email/spool /email/spool
+/cookbook/email/testing /email/testing
+/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
+/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
+/cookbook/form/direct_submit /form/direct_submit
+/cookbook/form/dynamic_form_modification /form/dynamic_form_modification
+/cookbook/form/form_collections /form/form_collections
+/cookbook/form/form_customization /form/form_customization
+/cookbook/form/index /forms
+/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
+/cookbook/frontend/index /frontend
+/cookbook/install/unstable_versions /setup/unstable_versions
+/cookbook/install/bundles /setup/bundles
+/cookbook/install/index /setup
+/cookbook/install/upgrade_major /setup/upgrade_major
+/cookbook/install/upgrade_minor /setup/upgrade_minor
+/cookbook/install/upgrade_patch /setup/upgrade_patch
+/cookbook/logging/channels_handlers /logging/channels_handlers
+/cookbook/logging/index /logging
+/cookbook/logging/monolog /logging
+/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#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 /components/psr7
+/cookbook/request/index /request
+/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
+/cookbook/routing/external_resources /routing/external_resources
+/cookbook/routing/extra_information /routing/extra_information
+/cookbook/routing/index /routing
+/cookbook/routing/method_parameters /routing/requirements
+/cookbook/routing/optional_placeholders /routing/optional_placeholders
+/cookbook/routing/redirect_in_config /routing/redirect_in_config
+/cookbook/routing/redirect_trailing_slash /routing/redirect_trailing_slash
+/cookbook/routing/requirements /routing/requirements
+/cookbook/routing/routing_from_database /routing/routing_from_database
+/cookbook/routing/scheme /routing/scheme
+/cookbook/routing/service_container_parameters /routing/service_container_parameters
+/cookbook/routing/slash_in_parameter /routing/slash_in_parameter
+/cookbook/security/access_control /security/access_control
+/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
+/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
+/cookbook/security/entity_provider /security/entity_provider
+/cookbook/security/firewall_restriction /security/firewall_restriction
+/cookbook/security/force_https /security/force_https
+/cookbook/security/form_login /security/form_login
+/cookbook/security/form_login_setup /security/form_login_setup
+/cookbook/security/guard-authentication /security/guard_authentication
+/cookbook/security/host_restriction /security/host_restriction
+/cookbook/security/impersonating_user /security/impersonating_user
+/cookbook/security/ldap /security/ldap
+/cookbook/security/multiple_guard_authenticators /security/multiple_guard_authenticators
+/cookbook/security/index /security
+/cookbook/security/multiple_user_providers /security/multiple_user_providers
+/cookbook/security/named_encoders /security/named_encoders
+/cookbook/security/pre_authenticated /security/pre_authenticated
+/cookbook/security/remember_me /security/remember_me
+/cookbook/security/securing_services /security/securing_services
+/cookbook/security/target_path /security/target_path
+/cookbook/security/user_checkers /security/user_checkers
+/cookbook/security/voters /security/voters
+/cookbook/serializer /serializer
+/cookbook/service_container/compiler_passes /service_container/compiler_passes
+/cookbook/service_container/index /service_container
+/cookbook/service_container/scopes /service_container/scopes
+/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 /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#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
+/cookbook/templating/render_without_controller /templating/render_without_controller
+/cookbook/templating/twig_extension /templating/twig_extension
+/cookbook/testing/bootstrap /testing/bootstrap
+/cookbook/testing/database /testing/database
+/cookbook/testing/doctrine /testing/doctrine
+/cookbook/testing/http_authentication /testing/http_authentication
+/cookbook/testing/index /testing
+/cookbook/testing/insulating_clients /testing/insulating_clients
+/cookbook/testing/profiling /testing/profiling
+/cookbook/testing/simulating_authentication /testing/simulating_authentication
+/cookbook/upgrade/bundles /upgrade/patch_version
+/cookbook/upgrade/index /setup/upgrade_major
+/cookbook/upgrade/major_version /setup/upgrade_minor
+/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 /form/validation_group_service_resolver
+/cookbook/validation/index /validation
+/cookbook/validation/severity /validation/severity
+/cookbook/web_server/built_in /setup/built_in_web_server
+/cookbook/web_server/index /setup/built_in_web_server
+/cookbook/web_services/index /controller/soap_web_service
+/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
+/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 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
+/components/dependency_injection/factories /service_container/factories
+/components/dependency_injection/lazy_services /service_container/lazy_services
+/components/dependency_injection/parameters /service_container/parameters
+/components/dependency_injection/parentservices /service_container/parent_services
+/components/dependency_injection/parent_services /service_container/parent_services
+/components/dependency_injection/synthetic_services /service_container/synthetic_services
+/components/dependency_injection/tags /service_container/tags
+/components/dependency_injection/types /service_container/injection_types
+/components/event_dispatcher/index /components/event_dispatcher
+/components/event_dispatcher/introduction /components/event_dispatcher
+/components/expression_language/introduction /components/expression_language
+/components/expression_language/index /components/expression_language
+/components/filesystem/introduction /components/filesystem
+/components/filesystem/index /components/filesystem
+/components/form/form_events /form/events
+/components/form/introduction /components/form
+/components/form/index /components/form
+/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 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 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
+/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
+/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
new file mode 100644
index 00000000000..4ba2c0c2b57
Binary files /dev/null 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
new file mode 100644
index 00000000000..96c5c316739
Binary files /dev/null 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
new file mode 100644
index 00000000000..48f6c7258d4
Binary files /dev/null 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
new file mode 100644
index 00000000000..abdff9812b0
Binary files /dev/null and b/_images/components/console/process-helper-verbose.png differ
diff --git a/_images/components/console/progressbar.gif b/_images/components/console/progressbar.gif
new file mode 100644
index 00000000000..0746e399354
Binary files /dev/null and b/_images/components/console/progressbar.gif 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/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/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/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/01-simple.png b/_images/components/var_dumper/01-simple.png
new file mode 100644
index 00000000000..a4d03147667
Binary files /dev/null and b/_images/components/var_dumper/01-simple.png differ
diff --git a/_images/components/var_dumper/02-multi-line-str.png b/_images/components/var_dumper/02-multi-line-str.png
new file mode 100644
index 00000000000..b40949bd981
Binary files /dev/null and b/_images/components/var_dumper/02-multi-line-str.png differ
diff --git a/_images/components/var_dumper/03-object.png b/_images/components/var_dumper/03-object.png
new file mode 100644
index 00000000000..47fc5e5e245
Binary files /dev/null and b/_images/components/var_dumper/03-object.png differ
diff --git a/_images/components/var_dumper/04-dynamic-property.png b/_images/components/var_dumper/04-dynamic-property.png
new file mode 100644
index 00000000000..de7938c20cf
Binary files /dev/null and b/_images/components/var_dumper/04-dynamic-property.png differ
diff --git a/_images/components/var_dumper/05-soft-ref.png b/_images/components/var_dumper/05-soft-ref.png
new file mode 100644
index 00000000000..964af97ffd3
Binary files /dev/null and b/_images/components/var_dumper/05-soft-ref.png differ
diff --git a/_images/components/var_dumper/06-constants.png b/_images/components/var_dumper/06-constants.png
new file mode 100644
index 00000000000..26c735bd613
Binary files /dev/null and b/_images/components/var_dumper/06-constants.png differ
diff --git a/_images/components/var_dumper/07-hard-ref.png b/_images/components/var_dumper/07-hard-ref.png
new file mode 100644
index 00000000000..02dc17c9c40
Binary files /dev/null and b/_images/components/var_dumper/07-hard-ref.png differ
diff --git a/_images/components/var_dumper/08-virtual-property.png b/_images/components/var_dumper/08-virtual-property.png
new file mode 100644
index 00000000000..564a2731ec1
Binary files /dev/null and b/_images/components/var_dumper/08-virtual-property.png differ
diff --git a/_images/components/var_dumper/09-cut.png b/_images/components/var_dumper/09-cut.png
new file mode 100644
index 00000000000..5229f48820c
Binary files /dev/null and b/_images/components/var_dumper/09-cut.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
new file mode 100644
index 00000000000..b7f51eabb43
Binary files /dev/null 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/job_application.png b/_images/components/workflow/job_application.png
new file mode 100644
index 00000000000..9c5e6792ae9
Binary files /dev/null and b/_images/components/workflow/job_application.png differ
diff --git a/_images/components/workflow/pull_request.png b/_images/components/workflow/pull_request.png
new file mode 100644
index 00000000000..692a95345ae
Binary files /dev/null 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/simple.png b/_images/components/workflow/simple.png
new file mode 100644
index 00000000000..ed158d5cc7a
Binary files /dev/null and b/_images/components/workflow/simple.png differ
diff --git a/_images/components/workflow/states_transitions.png b/_images/components/workflow/states_transitions.png
new file mode 100644
index 00000000000..d1f54391afd
Binary files /dev/null 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
new file mode 100644
index 00000000000..43b6842ffc2
Binary files /dev/null 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
new file mode 100644
index 00000000000..b739497f70f
Binary files /dev/null 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
new file mode 100644
index 00000000000..791901b8ec6
Binary files /dev/null and b/_images/contributing/docs-pull-request-change-base.png differ
diff --git a/_images/controller/error_pages/errors-in-prod-environment.png b/_images/controller/error_pages/errors-in-prod-environment.png
new file mode 100644
index 00000000000..808d0d70028
Binary files /dev/null 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
new file mode 100644
index 00000000000..e1fba2bebf9
Binary files /dev/null and b/_images/controller/error_pages/exceptions-in-dev-environment.png differ
diff --git a/_images/deployment/azure-website/step-01.png b/_images/deployment/azure-website/step-01.png
new file mode 100644
index 00000000000..ef60db66ab2
Binary files /dev/null and b/_images/deployment/azure-website/step-01.png differ
diff --git a/_images/deployment/azure-website/step-02.png b/_images/deployment/azure-website/step-02.png
new file mode 100644
index 00000000000..fe38cf45be3
Binary files /dev/null and b/_images/deployment/azure-website/step-02.png differ
diff --git a/_images/deployment/azure-website/step-03.png b/_images/deployment/azure-website/step-03.png
new file mode 100644
index 00000000000..6fc0789cac9
Binary files /dev/null and b/_images/deployment/azure-website/step-03.png differ
diff --git a/_images/deployment/azure-website/step-04.png b/_images/deployment/azure-website/step-04.png
new file mode 100644
index 00000000000..a16d8f07a86
Binary files /dev/null and b/_images/deployment/azure-website/step-04.png differ
diff --git a/_images/deployment/azure-website/step-05.png b/_images/deployment/azure-website/step-05.png
new file mode 100644
index 00000000000..8da32f7ab67
Binary files /dev/null and b/_images/deployment/azure-website/step-05.png differ
diff --git a/_images/deployment/azure-website/step-06.png b/_images/deployment/azure-website/step-06.png
new file mode 100644
index 00000000000..067ff4e767a
Binary files /dev/null and b/_images/deployment/azure-website/step-06.png differ
diff --git a/_images/deployment/azure-website/step-07.png b/_images/deployment/azure-website/step-07.png
new file mode 100644
index 00000000000..7acffd2c782
Binary files /dev/null and b/_images/deployment/azure-website/step-07.png differ
diff --git a/_images/deployment/azure-website/step-08.png b/_images/deployment/azure-website/step-08.png
new file mode 100644
index 00000000000..cb106db5c02
Binary files /dev/null and b/_images/deployment/azure-website/step-08.png differ
diff --git a/_images/deployment/azure-website/step-09.png b/_images/deployment/azure-website/step-09.png
new file mode 100644
index 00000000000..5005531fb09
Binary files /dev/null and b/_images/deployment/azure-website/step-09.png differ
diff --git a/_images/deployment/azure-website/step-10.png b/_images/deployment/azure-website/step-10.png
new file mode 100644
index 00000000000..e9a7d8fdff8
Binary files /dev/null and b/_images/deployment/azure-website/step-10.png differ
diff --git a/_images/deployment/azure-website/step-11.png b/_images/deployment/azure-website/step-11.png
new file mode 100644
index 00000000000..48b1c2992e1
Binary files /dev/null and b/_images/deployment/azure-website/step-11.png differ
diff --git a/_images/deployment/azure-website/step-12.png b/_images/deployment/azure-website/step-12.png
new file mode 100644
index 00000000000..85f8f54d142
Binary files /dev/null and b/_images/deployment/azure-website/step-12.png differ
diff --git a/_images/deployment/azure-website/step-13.png b/_images/deployment/azure-website/step-13.png
new file mode 100644
index 00000000000..49aac465fd7
Binary files /dev/null and b/_images/deployment/azure-website/step-13.png differ
diff --git a/_images/deployment/azure-website/step-14.png b/_images/deployment/azure-website/step-14.png
new file mode 100644
index 00000000000..8e6c3ed3a5e
Binary files /dev/null and b/_images/deployment/azure-website/step-14.png differ
diff --git a/_images/deployment/azure-website/step-15.png b/_images/deployment/azure-website/step-15.png
new file mode 100644
index 00000000000..c8d5bce96d3
Binary files /dev/null and b/_images/deployment/azure-website/step-15.png differ
diff --git a/_images/deployment/azure-website/step-16.png b/_images/deployment/azure-website/step-16.png
new file mode 100644
index 00000000000..da7d4bebde7
Binary files /dev/null and b/_images/deployment/azure-website/step-16.png differ
diff --git a/_images/doctrine/doctrine_web_debug_toolbar.png b/_images/doctrine/doctrine_web_debug_toolbar.png
new file mode 100644
index 00000000000..8103162e591
Binary files /dev/null and b/_images/doctrine/doctrine_web_debug_toolbar.png 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.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.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.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-2.png b/_images/form/simple-form-2.png
new file mode 100644
index 00000000000..d50028fc1f7
Binary files /dev/null and b/_images/form/simple-form-2.png differ
diff --git a/_images/form/simple-form.png b/_images/form/simple-form.png
new file mode 100644
index 00000000000..1dced444561
Binary files /dev/null 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.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.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.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
new file mode 100644
index 00000000000..3d3f9a98a4a
Binary files /dev/null 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/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/reference/form/choice-example1.png b/_images/reference/form/choice-example1.png
new file mode 100644
index 00000000000..00e47d0bb27
Binary files /dev/null and b/_images/reference/form/choice-example1.png differ
diff --git a/_images/reference/form/choice-example2.png b/_images/reference/form/choice-example2.png
new file mode 100644
index 00000000000..147d82bcfca
Binary files /dev/null and b/_images/reference/form/choice-example2.png differ
diff --git a/_images/reference/form/choice-example3.png b/_images/reference/form/choice-example3.png
new file mode 100644
index 00000000000..232f8519fee
Binary files /dev/null and b/_images/reference/form/choice-example3.png differ
diff --git a/_images/reference/form/choice-example4.png b/_images/reference/form/choice-example4.png
new file mode 100644
index 00000000000..7f6071d3532
Binary files /dev/null and b/_images/reference/form/choice-example4.png differ
diff --git a/_images/reference/form/choice-example5.png b/_images/reference/form/choice-example5.png
new file mode 100644
index 00000000000..188eeeec234
Binary files /dev/null and b/_images/reference/form/choice-example5.png differ
diff --git a/_images/security/anonymous_wdt.png b/_images/security/anonymous_wdt.png
new file mode 100644
index 00000000000..80736afce39
Binary files /dev/null and b/_images/security/anonymous_wdt.png 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/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
new file mode 100644
index 00000000000..b51e1cafba1
Binary files /dev/null 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/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/_rewrite_rule_tip.rst.inc b/_includes/_rewrite_rule_tip.rst.inc
new file mode 100644
index 00000000000..fe69882c4f7
--- /dev/null
+++ b/_includes/_rewrite_rule_tip.rst.inc
@@ -0,0 +1,6 @@
+.. tip::
+
+ By using rewrite rules in your
+ :doc:`web server configuration `,
+ the ``index.php`` won't be needed and you will have beautiful, clean URLs
+ (e.g. ``/show``).
diff --git a/best_practices.rst b/best_practices.rst
new file mode 100644
index 00000000000..2c393cae9c6
--- /dev/null
+++ b/best_practices.rst
@@ -0,0 +1,460 @@
+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.
+
+Additionally, you should use the ``anonymous`` key under your firewall. If you
+require users to be logged in for different sections of your site, use the
+:doc:`access_control ` option.
+
+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/book/bundles.rst b/book/bundles.rst
deleted file mode 100644
index d9a6810e620..00000000000
--- a/book/bundles.rst
+++ /dev/null
@@ -1,257 +0,0 @@
-.. index::
- single: Bundles; Best Practices
-
-Bundles
-=======
-
-A bundle is a directory that has a well-defined structure and can host anything
-from classes to controllers and web resources. Even if bundles are very
-flexible, you should follow some best practices if you want to distribute them.
-
-.. index::
- pair: Bundles; Naming Conventions
-
-.. _bundles-naming-conventions:
-
-Bundle Name
------------
-
-A bundle is also a PHP namespace. The namespace must follow the technical
-interoperability `standards`_ for PHP 5.3 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 namespace becomes a bundle as soon as you add a bundle class to it. The
-bundle class name must follow these simple rules:
-
-* Use only alphanumeric characters and underscores;
-* Use a CamelCased name;
-* Use a descriptive and short name (no more than 2 words);
-* Prefix the name with the concatenation of the vendor (and optionally the
- category namespaces);
-* Suffix the name with ``Bundle``.
-
-Here are some valid bundle namespaces and class names:
-
-=================================== ==============================
-Namespace Bundle Class Name
-=================================== ==============================
-``Acme\Bundle\BlogBundle`` ``AcmeBlogBundle``
-``Acme\Bundle\Social\BlogBundle`` ``AcmeSocialBlogBundle``
-``Acme\BlogBundle`` ``AcmeBlogBundle``
-=================================== ==============================
-
-By convention, the ``getName()`` method of the bundle class should return the
-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).
-
-.. note::
-
- Symfony2 core Bundles do not prefix the Bundle class with ``Symfony``
- and always add a ``Bundle`` subnamespace; for example:
- :class:``Symfony\Bundle\FrameworkBundle\FrameworkBundle``.
-
-Directory Structure
--------------------
-
-The basic directory structure of a ``HelloBundle`` bundle must read as
-follows:
-
-.. code-block:: text
-
- XXX/...
- HelloBundle/
- HelloBundle.php
- Controller/
- Resources/
- meta/
- LICENSE
- config/
- doc/
- index.rst
- translations/
- views/
- public/
- Tests/
-
-The ``XXX`` directory(ies) reflects the namespace structure of the bundle.
-
-The following files are mandatory:
-
-* ``HelloBundle.php``;
-* ``Resources/meta/LICENSE``: The full license for the code;
-* ``Resources/doc/index.rst``: The root file for the Bundle documentation.
-
-.. note::
-
- These conventions ensure that automated tools can rely on this default
- structure to work.
-
-The depth of sub-directories should be kept to the minimal for most used
-classes and files (2 levels at a maximum). More levels can be defined for
-non-strategic, less-used files.
-
-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
-can generate files in the bundle directory structure, but only if the generated
-files are going to be part of the repository.
-
-The following classes and files have specific emplacements:
-
-========================= ===========================
-Type Directory
-========================= ===========================
-Controllers ``Controller/``
-Translation files ``Resources/translations/``
-Templates ``Resources/views/``
-Unit and Functional Tests ``Tests/``
-Web Resources ``Resources/public/``
-Configuration ``Resources/config/``
-Commands ``Command/``
-========================= ===========================
-
-Classes
--------
-
-The bundle directory structure is used as the namespace hierarchy. For
-instance, a ``HelloController`` controller is stored in
-``Bundle/HelloBundle/Controller/HelloController.php`` and the fully qualified
-class name is ``Bundle\HelloBundle\Controller\HelloController``.
-
-All classes and files must follow the Symfony2 coding :doc:`standards
-`.
-
-Some classes should be seen as facades and should be as short as possible, like
-Commands, Helpers, Listeners, and Controllers.
-
-Classes that connects to the Event Dispatcher should be suffixed with
-``Listener``.
-
-Exceptions classes should be stored in an ``Exception`` sub-namespace.
-
-Vendors
--------
-
-A bundle must not embed third-party PHP libraries. It should rely on the
-standard Symfony2 autoloading instead.
-
-A bundle should not embed third-party libraries written in JavaScript, CSS, or
-any other language.
-
-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 test suite must be executable with a simple ``phpunit`` command run from
- a sample application;
-* The functional tests should only be used to test the response output and
- some profiling information if you have some;
-* The code coverage should at least covers 95% of the code base.
-
-.. note::
- A test suite must not contain ``AllTests.php`` scripts, but must rely on the
- existence of a ``phpunit.xml.dist`` file.
-
-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.
-
-Controllers
------------
-
-As a best practice, controllers in a bundle that's meant to be distributed
-to others must not extend the
-:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` base class.
-They can implement
-:class:`Symfony\\Foundation\\DependencyInjection\\ContainerAwareInterface` or
-extend :class:`Symfony\\Foundation\\DependencyInjection\\ContainerAware`
-instead.
-
-.. note::
-
- If you have a look at
- :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` methods,
- you will see that they are only nice shortcuts to ease the learning curve.
-
-Templates
----------
-
-If a bundle provides templates, they must use Twig. A bundle must not provide
-a main layout, except if it provides a full working application.
-
-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 (``bundle.hello``).
-
-A bundle must not override existing messages from another bundle.
-
-Configuration
--------------
-
-To provide more flexibility, a bundle can provide configurable settings by
-using the Symfony2 built-in mechanisms.
-
-For simple configuration settings, rely on the default ``parameters`` entry of
-the Symfony2 configuration. Symfony2 parameters are simple key/value pairs; a
-value being any valid PHP value. Each parameter name should start with a
-lower-cased short version of the bundle name using underscores (``acme_hello``
-for ``AcmeHelloBundle``, or ``acme_social_blog`` for ``Acme\Social\BlogBundle``
-for instance), though this is just a best-practice suggestion. The rest of
-the parameter name will use a period (``.``) to separate different parts
-(e.g. ``acme_hello.email.from``).
-
-The end user can provide values in any configuration file:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # app/config/config.yml
- parameters:
- acme_hello.email.from: fabien@example.com
-
- .. code-block:: xml
-
-
-
- fabien@example.com
-
-
- .. code-block:: php
-
- // app/config/config.php
- $container->setParameter('acme_hello.email.from', 'fabien@example.com');
-
- .. code-block:: ini
-
- [parameters]
- acme_hello.email.from = fabien@example.com
-
-Retrieve the configuration parameters in your code from the container::
-
- $container->getParameter('acme_hello.email.from');
-
-Even if this mechanism is simple enough, you are highly encouraged to use the
-semantic configuration described in the cookbook.
-
-Learn more from the Cookbook
-----------------------------
-
-* :doc:`/cookbook/bundles/extension`
-
-.. _standards: http://groups.google.com/group/php-standards/web/psr-0-final-proposal
diff --git a/book/controller.rst b/book/controller.rst
deleted file mode 100644
index 5c6a2fe4387..00000000000
--- a/book/controller.rst
+++ /dev/null
@@ -1,717 +0,0 @@
-.. index::
- single: Controller
-
-The Controller
-==============
-
-A controller is a PHP function you create that takes information from the
-HTTP request and constructs and returns an HTTP response (as a Symfony2
-``Response`` object). The response could be an HTML page, an XML document,
-a serialized JSON array, an image, a redirect, a 404 error or anything else
-you can dream up. The controller contains whatever arbitrary logic *your
-application* needs to create that response.
-
-Along the way, your controller might read information from the request, load
-a database resource, send an email, or set information on the user's session.
-But in all cases, the controller's final job is to return the ``Response``
-object that will be delivered back to the client. There's no magic and no
-other requirements to worry about. Here are a few common examples:
-
-* *Controller A* prepares a ``Response`` object representing the content
- for the homepage of the site.
-
-* *Controller B* reads the ``slug`` parameter from the request to load a
- blog entry from the database and create a ``Response`` object displaying
- that blog. If the ``slug`` can't be found in the database, it creates and
- returns a ``Response`` object with a 404 status code.
-
-* *Controller C* handles the form submission of a contact form. It reads
- the form information from the request, saves the contact information to
- the database and emails the contact information to the webmaster. Finally,
- it creates a ``Response`` object that redirects the client's browser to
- the contact form "thank you" page.
-
-.. index::
- single: Controller; Request-controller-response lifecycle
-
-Requests, Controller, Response Lifecycle
-----------------------------------------
-
-Every request handled by a Symfony2 project goes through the same basic lifecycle.
-The framework takes care of the repetitive tasks and ultimately executes a
-controller, which houses your custom application code:
-
-* Each request is handled by a single front controller file (e.g. ``app.php``
- or ``index.php``) that's responsible for bootstrapping the framework;
-
-* The ``Router`` reads the request information, matches that information to
- a specific route, and determines from that route which controller should
- be called;
-
-* The controller is executed and the code inside the controller creates and
- returns a ``Response`` object;
-
-* The HTTP headers and content of the ``Response`` object are sent back to
- the client.
-
-Creating a page is as easy as creating a controller and making a route that
-maps a URI to that controller.
-
-.. note::
-
- Though similarly named, a "front controller" is different from the
- "controllers" we'll talk about in this chapter. A front controller
- is a short PHP file that lives in your web directory and through which
- all requests are directed. A typical application will have a production
- front controller (e.g. ``app.php``) and a development front controller
- (e.g. ``app_dev.php``). You'll likely never need to edit, view or worry
- about the front controllers in your application.
-
-.. index::
- single: Controller; Simple example
-
-A Simple Controller
--------------------
-
-The controller is a PHP callable responsible for returning a representation
-of the resource (most of the time an HTML representation). Though a controller
-can be any PHP callable (a function, a method on an object, or a ``Closure``),
-in Symfony2, a controller is usually a single method inside a controller
-object. Controllers are also called *actions*.
-
-.. code-block:: php
-
- // src/Acme/HelloBundle/Controller/HelloController.php
-
- namespace Acme\HelloBundle\Controller;
- use Symfony\Component\HttpFoundation\Response;
-
- class HelloController
- {
- public function indexAction($name)
- {
- return new Response('Hello '.$name.'!');
- }
- }
-
-.. tip::
-
- Note that the *controller* is the ``indexAction`` method, which lives
- inside a *controller class* (``HelloController``). Don't be confused
- by the naming: a *controller class* is simply a convenient way to group
- several controllers together. Typically, the controller class will house
- several controllers (e.g. ``updateAction``, ``deleteAction``, etc). A
- controller is also sometimes referred to as an *action*.
-
-This controller is pretty straightforward, but let's walk through it:
-
-* *line 3*: Symfony2 takes advantage of PHP 5.3 namespace functionality to
- namespace the entire controller class. The ``use`` keyword imports the
- ``Response`` class, which our controller must return.
-
-* *line 6*: The class name is the concatenation of a name for the controller
- class and the word ``Controller``. This is a convention that provides consistency
- to controllers and allows them to be referenced only by the first part of
- the name (i.e. ``Hello``) in the routing configuration.
-
-* *line 8*: Each action in a controller class is suffixed with ``Action``
- and is referenced in the routing configuration by the action's name (``index``).
- In the next section, we'll use a route to map a URI to this action and
- show how the route's placeholders (``{name}``) become arguments on the
- action (``$name``).
-
-* *line 10*: The controller creates and returns a ``Response`` object.
-
-.. index::
- single: Controller; Routes and controllers
-
-Mapping a URI to a Controller
------------------------------
-
-Our new controller returns a simple HTML page. To render this controller
-at a specific URL, we need to create a route to it.
-
-We'll talk about the ``Routing`` component in detail in the :doc:`Routing chapter`,
-but let's create a simple route to our controller:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # src/Acme/HelloBundle/Resources/config/routing.yml
- hello:
- pattern: /hello/{name}
- defaults: { _controller: AcmeHelloBundle:Hello:index }
-
- .. code-block:: xml
-
-
-
- AcmeHelloBundle:Hello:index
-
-
- .. code-block:: php
-
- // src/Acme/HelloBundle/Resources/config/routing.php
- $collection->add('hello', new Route('/hello/{name}', array(
- '_controller' => 'AcmeHelloBundle:Hello:index',
- )));
-
-Going to ``/hello/ryan`` now executes the ``HelloController::indexAction()``
-controller and passes in ``ryan`` for the ``$name`` variable. Creating a
-"page" means simply creating a controller method and associated route. There's
-no hidden layers or behind-the-scenes magic.
-
-Notice the syntax used to refer to the controller: ``AcmeHelloBundle:Hello:index``.
-Symfony2 uses a flexible string notation to refer to different controllers.
-This is the most common syntax and tells Symfony2 to look for a controller
-class called ``HelloController`` inside a bundle named ``HelloBundle``. The
-method ``indexAction()`` is then executed.
-
-For more details on the string format used to reference different controllers,
-see :ref:`controller-string-syntax`.
-
-.. tip::
-
- Notice that since our controller lives in the ``HelloBundle``, we've
- placed the routing configuration inside the ``HelloBundle`` to stay
- organized. To load routing configuration that lives inside a bundle, it
- must be imported from your application's main routing resource. See
- :ref:`routing-include-external-resources` for more information.
-
-.. index::
- single: Controller; Route parameters as controller arguments
-
-.. _route-parameters-controller-arguments:
-
-Route Parameters as Controller Arguments
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-We already know now that the ``_controller`` parameter ``AcmeHelloBundle:Hello:index``
-refers to a ``HelloController::indexAction()`` method that lives inside the
-``HelloBundle`` bundle. What's more interesting is the arguments that are
-passed to that method:
-
-.. code-block:: php
-
-
-
- AcmeHelloBundle:Hello:index
- green
-
-
- .. code-block:: php
-
- // src/Acme/HelloBundle/Resources/config/routing.php
- $collection->add('hello', new Route('/hello/{first_name}/{last_name}', array(
- '_controller' => 'AcmeHelloBundle:Hello:index',
- 'color' => 'green',
- )));
-
-The controller for this can take several arguments::
-
- public function indexAction($first_name, $last_name, $color)
- {
- // ...
- }
-
-Notice that both placeholder variables (``{{first_name}}``, ``{{last_name}}``)
-as well as the default ``color`` variable are available as arguments in the
-controller. When a route is matched, the placeholder variables are merged
-with the ``defaults`` to make one array that's available to your controller.
-
-Mapping route parameters to controller arguments is easy and flexible. Keep
-the following guidelines in mind while you develop.
-
-The order of the controller arguments does not matter.
-......................................................
-
-Symfony2 is able to matches the parameter names from the route to the variable
-names in the controller method's signature. In other words, it realizes that
-the ``last_name`` parameter matches up with the ``$last_name`` argument.
-The arguments of the controller could be totally reordered and still work
-perfectly::
-
- public function indexAction($last_name, $color, $first_name)
- {
- // ..
- }
-
-Each required controller argument must match up with a routing parameter.
-.........................................................................
-
-The following would throw a ``RuntimeException`` because there is no ``foo``
-parameter defined in the route::
-
- public function indexAction($first_name, $last_name, $color, $foo)
- {
- // ..
- }
-
-Making the argument optional, however, is perfectly ok. The following
-example would not throw an exception::
-
- public function indexAction($first_name, $last_name, $color, $foo = 'bar')
- {
- // ..
- }
-
-Not all routing parameters need to be arguments on your controller.
-...................................................................
-
-If, for example, the ``last_name`` weren't important for your controller,
-you could omit it entirely::
-
- public function indexAction($first_name, $color)
- {
- // ..
- }
-
-In fact, the ``_controller`` route parameter itself is technically available
-as a controller argument since it's in the ``defaults`` of the route. Of
-course, it's generally not very useful, so it's omitted from our controller.
-
-.. tip::
- Every route also has a special ``_route`` parameter, which is equal to
- the name of the route that was matched (e.g. ``hello``). Though not usually
- useful, this is equally available as a controller argument.
-
-The Base Controller Class
--------------------------
-
-For convenience, Symfony2 comes with a base ``Controller`` class that assists
-with some of the most common controller tasks and gives your controller class
-access to any resource it might need. By extending this ``Controller`` class,
-you can take advantage of several helper methods.
-
-Add the ``use`` statement atop the ``Controller`` class and then modify the
-``HelloController`` to extend it. That's all there is to it.
-
-.. code-block:: php
-
- // src/Acme/HelloBundle/Controller/HelloController.php
-
- namespace Acme\HelloBundle\Controller;
- use Symfony\Bundle\FrameworkBundle\Controller\Controller;
- use Symfony\Component\HttpFoundation\Response;
-
- class HelloController extends Controller
- {
- public function indexAction($name)
- {
- return new Response('Hello '.$name.'!');
- }
- }
-
-So far, extending the base ``Controller`` class hasn't changed anything. In
-the next section, we'll walk through several helper methods that the base
-controller class makes available. These methods are just shortcuts to using
-core Symfony2 functionality that's available to you with or without the use of
-the base ``Controller`` class. A great way to see the core functionality in
-action is to look in the
-:class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller` class
-itself.
-
-.. tip::
-
- Extending the base class is a *choice* in Symfony; it contains useful
- shortcuts but nothing mandatory. You can also extend
- :class:`Symfony\\Component\\DependencyInjection\\ContainerAware`. The
- service container object will then be accessible via the ``container``
- property and this is the only object you need to create any controller.
-
-.. note::
-
- You can also define your :doc:`Controllers as Services
- `.
-
-.. index::
- single: Controller; Common Tasks
-
-Common Controller Tasks
------------------------
-
-Though a controller can do virtually anything, most controllers will perform
-the same basic tasks over and over again. These tasks, such as redirecting,
-forwarding, rendering templates and accessing core services, are very easy
-to manage in Symfony2.
-
-.. index::
- single: Controller; Redirecting
-
-Redirecting
-~~~~~~~~~~~
-
-If you want to redirect the user to another page, use a special ``RedirectResponse``
-class, which is designed specifically to redirect the user to another URL::
-
- // ...
- use Symfony\Component\HttpFoundation\RedirectResponse;
-
- class HelloController extends Controller
- {
- public function indexAction()
- {
- return new RedirectResponse($this->generateUrl('hello', array('name' => 'Lucas')));
- }
- }
-
-The ``generateUrl()`` method is just a shortcut that calls ``generate()``
-on the ``router`` service. It takes the route name and an array of parameters
-as arguments and returns the associated friendly URL. See the :doc:`Routing `
-chapter for more information.
-
-By default, the ``redirect`` method does a 302 (temporary) redirect. To perform
-a 301 (permanent) redirect, modify the second argument::
-
- public function indexAction()
- {
- return new RedirectResponse($this->generateUrl('hello', array('name' => 'Lucas')), 301);
- }
-
-.. index::
- single: Controller; Forwarding
-
-Forwarding
-~~~~~~~~~~
-
-You can also easily forward to another action internally with the ``forward()``
-method. Instead of redirecting the user's browser, it makes an internal sub-request,
-and calls the specified controller. The ``forward()`` method returns the ``Response``
-object to allow for further modification if the need arises. That ``Response``
-object is the end-product of the internal sub-request::
-
- public function indexAction($name)
- {
- $response = $this->forward('AcmeHelloBundle:Hello:fancy', array(
- 'name' => $name,
- 'color' => 'green'
- ));
-
- // further modify the response or return it directly
-
- return $response;
- }
-
-Notice that the `forward()` method uses the same string representation of
-the controller used in the routing configuration. In this case, the target
-controller class will be ``HelloController`` inside some ``AcmeHelloBundle``.
-The array passed to the method becomes the arguments on the resulting controller.
-This same interface is used when embedding controllers into templates (see
-:ref:`templating-embedding-controller`). The target controller method should
-look something like the following::
-
- public function fancyAction($name, $color)
- {
- // ... create and return a Response object
- }
-
-And just like when creating a controller for a route, the order of the arguments
-to ``fancyAction`` doesn't matter. Symfony2 matches the index key names
-(e.g. ``name``) with the method argument names (e.g. ``$name``). If you
-change the order of the arguments, Symfony2 will still pass the correct
-value to each variable.
-
-.. tip::
-
- Like other base ``Controller`` methods, the ``forward`` method is just
- a shortcut for core Symfony2 functionality. A forward can be accomplished
- directly via the ``http_kernel`` service. A forward returns a ``Response``
- object::
-
- $httpKernel = $this->container->get('http_kernel');
- $response = $httpKernel->forward('AcmeHelloBundle:Hello:fancy', array(
- 'name' => $name,
- 'color' => 'green',
- ));
-
-.. index::
- single: Controller; Rendering templates
-
-.. _controller-rendering-templates:
-
-Rendering Templates
-~~~~~~~~~~~~~~~~~~~
-
-Though not a requirement, most controllers will ultimately render a template
-that's responsible for generating the HTML (or other format) for the controller.
-The ``renderView()`` method renders a template and returns its content. The
-content from the template can be used to create a ``Response`` object::
-
- $content = $this->renderView('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));
-
- return new Response($content);
-
-This can even be done in just one step with the ``render()`` method, which
-returns a ``Response`` object with the content from the template::
-
- return $this->render('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));
-
-In both cases, the ``Resources/views/Hello/index.html.twig`` template inside
-the ``AcmeHelloBundle`` will be rendered.
-
-The Symfony templating engine is explained in great detail in the
-:doc:`Templating ` chapter.
-
-.. tip::
-
- The ``renderView`` method is a shortcut to direct use of the ``templating``
- service. The ``templating`` service can also be used directly::
-
- $templating = $this->get('templating');
- $content = $templating->render('AcmeHelloBundle:Hello:index.html.twig', array('name' => $name));
-
-.. index::
- single: Controller; Accessing services
-
-Accessing other Services
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-When extending the base controller class, you can access any Symfony2 service
-via the ``get()`` method. Here are several common services you might need::
-
- $request = $this->get('request');
-
- $response = $this->get('response');
-
- $templating = $this->get('templating');
-
- $router = $this->get('router');
-
- $mailer = $this->get('mailer');
-
-There are countless other services available and you are encouraged to define
-your own. For more information, see the :doc:`/book/service_container`
-chapter.
-
-.. index::
- single: Controller; Managing errors
-
-Managing Errors
----------------
-
-When things are not found, you should play well with the HTTP protocol and
-return a 404 response. This is easily done by throwing a built-in HTTP
-exception:
-
-.. code-block:: php
-
- use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-
- public function indexAction()
- {
- $product = // retrieve the object from database
- if (!$product) {
- throw new NotFoundHttpException('The product does not exist.');
- }
-
- return $this->render(...);
- }
-
-The ``NotFoundHttpException`` will return a 404 HTTP response back to the
-browser. When viewing a page in debug mode, a full exception with stacktrace
-is displayed so that the cause of the exception can be easily tracked down.
-
-Of course, you're free to throw any ``Exception`` class in your controller
-- Symfony2 will automatically return a 500 HTTP response code.
-
-.. code-block:: php
-
- throw new \Exception('Something went wrong!');
-
-In every case, a styled error page is shown to the end user and a full debug
-error page is shown to the developer (when viewing the page in debug mode).
-Both of these error pages can be customized.
-
-.. note::
-
- Read the ":doc:`/cookbook/controller/error_pages`" recipe to learn more.
-
-.. index::
- single: Controller; The session
- single: Session
-
-Managing the Session
---------------------
-
-Even if the HTTP protocol is stateless, Symfony2 provides a nice session object
-that represents the client (be it a real person using a browser, a bot, or a
-web service). Between two requests, Symfony2 stores the attributes in a cookie
-by using the native PHP sessions.
-
-Storing and retrieving information from the session can be easily achieved
-from any controller::
-
- $session = $this->get('request')->getSession();
-
- // store an attribute for reuse during a later user request
- $session->set('foo', 'bar');
-
- // in another controller for another request
- $foo = $session->get('foo');
-
- // set the user locale
- $session->setLocale('fr');
-
-These attributes will remain on the user for the remainder of that user's
-session.
-
-.. index::
- single Session; Flash messages
-
-Flash Messages
-~~~~~~~~~~~~~~
-
-You can also store small messages that will be stored on the user's session
-for exactly one additional request. This is useful when processing a form:
-you want to redirect and have a special message shown on the *next* request.
-These types of messages are called "flash" messages.
-
-Let's show an example where we're processing a form submit::
-
- public function updateAction()
- {
- if ('POST' === $this->get('request')->getMethod()) {
- // do some sort of processing
-
- $this->get('session')->setFlash('notice', 'Your changes were saved!');
-
- return new RedirectResponse($this->generateUrl(...));
- }
-
- return $this->render(...);
- }
-
-After processing the request, the controller sets a ``notice`` flash message
-and then redirects. In the template of the next action, the following code
-could be used to render the message:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {% if app.session.hasFlash('notice') %}
-
-
-
-By design, flash messages are meant to only live for exactly one request
-(they're "gone in a flash"). They're designed to be used across redirects
-exactly as we've done in this example.
-
-.. index::
- single: Controller; Response
-
-The Response Object
--------------------
-
-The only requirement for a controller is to return a ``Response`` object. The
-:class:`Symfony\\Component\\HttpFoundation\\Response` class is a PHP
-abstraction around the HTTP response - the text-based message filled with HTTP
-headers and content that's sent back to the client::
-
- // create a simple Response with a 200 status code (the default)
- $response = new Response('Hello '.$name, 200);
-
- // create a JSON-response with a 200 status code
- $response = new Response(json_encode(array('name' => $name)));
- $response->headers->set('Content-Type', 'application/json');
-
-.. tip::
-
- The ``headers`` property is a
- :class:`Symfony\\Component\\HttpFoundation\\HeaderBag` object with several
- useful methods for reading and mutating the ``Response`` headers. The
- header names are normalized so that using ``Content-Type`` is equivalent
- to ``content-type`` or even ``content_type``.
-
-.. index::
- single: Controller; Request
-
-The Request Object
-------------------
-
-Besides the values of the routing placeholders, the controller also has access
-to the ``Request`` object when extending the base ``Controller`` class::
-
- $request = $this->get('request');
-
- $request->isXmlHttpRequest(); // is it an Ajax request?
-
- $request->getPreferredLanguage(array('en', 'fr'));
-
- $request->query->get('page'); // get a $_GET parameter
-
- $request->request->get('page'); // get a $_POST parameter
-
-Like the ``Response`` object, the request headers are stored in a ``HeaderBag``
-object and are easily accessible.
-
-.. index::
- single: Controller; Overview
-
-Overview
---------
-
-In Symfony, a controller is nothing more than a PHP function that contains
-whatever arbitrary logic is needed to create and return a ``Response`` object.
-The controller allows us to have an application with many pages while keeping
-the logic for each page organized into different controller classes and action
-methods.
-
-Symfony2 decides which controller should handle each request by matching
-a route and resolving the string format of its ``_controller`` parameter
-to a real Symfony2 controller. The arguments on that controller correspond
-to the parameters on the route, allowing your controller access to the information
-form the request.
-
-The controller can do anything and contain any logic, as long as it returns
-a ``Response`` object. If you extend the base ``Controller`` class, you
-instantly have access to all of the Symfony2 core service objects as well
-as shortcut methods to performing the most common tasks.
-
-Learn more from the Cookbook
-----------------------------
-
-* :doc:`/cookbook/controller/service`
-* :doc:`/cookbook/controller/error_pages`
diff --git a/book/doctrine/dbal/configuration.rst b/book/doctrine/dbal/configuration.rst
deleted file mode 100644
index 781099d76ae..00000000000
--- a/book/doctrine/dbal/configuration.rst
+++ /dev/null
@@ -1,135 +0,0 @@
-.. index::
- single: Configuration; Doctrine DBAL
- single: Doctrine; DBAL configuration
-
-Configuration
-=============
-
-One example configuration with a MySQL database could look like this following
-example:
-
-.. code-block:: yaml
-
- # app/config/config.yml
- doctrine:
- dbal:
- connections:
- default:
- driver: pdo_mysql
- dbname: Symfony2
- user: root
- password: null
-
-The DoctrineBundle supports all parameters that all the default doctrine drivers
-accept, converted to the XML or YAML naming standards that Symfony enforces.
-See the Doctrine DBAL `documentation`_ for more information. Additionally
-there are some Symfony-related options that you can configure. The following
-block shows all possible configuration keys without explaining their meaning
-further:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- doctrine:
- dbal:
- connections:
- default:
- dbname: database
- host: localhost
- port: 1234
- user: user
- password: secret
- driver: pdo_mysql
- driver_class: MyNamespace\MyDriverImpl
- options:
- foo: bar
- path: %kernel.data_dir%/data.sqlite
- memory: true
- unix_socket: /tmp/mysql.sock
- wrapper_class: MyDoctrineDbalConnectionWrapper
- charset: UTF8
- logging: %kernel.debug%
- platform_service: MyOwnDatabasePlatformService
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-There are also a bunch of dependency injection container parameters
-that allow you to specify which classes are used (with their default values):
-
-.. code-block:: yaml
-
- parameters:
- doctrine.dbal.logger_class: Symfony\Bundle\DoctrineBundle\Logger\DbalLogger
- doctrine.dbal.configuration_class: Doctrine\DBAL\Configuration
- doctrine.data_collector.class: Symfony\Bundle\DoctrineBundle\DataCollector\DoctrineDataCollector
- doctrine.dbal.event_manager_class: Doctrine\Common\EventManager
- doctrine.dbal.events.mysql_session_init.class: Doctrine\DBAL\Event\Listeners\MysqlSessionInit
- doctrine.dbal.events.oracle_session_init.class: Doctrine\DBAL\Event\Listeners\OracleSessionInit
-
-If you want to configure multiple connections you can do so by simply listing
-them under the key named ``connections``. All the parameters shown above
-can also be specified in the connections subkeys.
-
-.. code-block:: yaml
-
- doctrine:
- dbal:
- default_connection: default
- connections:
- default:
- dbname: Symfony2
- user: root
- password: null
- host: localhost
- customer:
- dbname: customer
- user: root
- password: null
- host: localhost
-
-If you have defined multiple connections you can use the
-``$this->get('doctrine.dbal.[connectionname]_connection')``
-as well but you must pass it an argument with the
-connection name that you want get
-
-.. code-block:: php
-
- class UserController extends Controller
- {
- public function indexAction()
- {
- $defaultConn1 = $this->get('doctrine.dbal.connection');
- $defaultConn2 = $this->get('doctrine.dbal.default_connection');
- // $defaultConn1 === $defaultConn2
-
- $customerConn = $this->get('doctrine.dbal.customer_connection');
- }
- }
-
-.. _documentation: http://www.doctrine-project.org/docs/dbal/2.0/en
diff --git a/book/doctrine/dbal/index.rst b/book/doctrine/dbal/index.rst
deleted file mode 100644
index c9bf74f606c..00000000000
--- a/book/doctrine/dbal/index.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-Database Abstraction Layer
-==========================
-
-Learn about Doctrine's Database Abstraction Layer and how it can help you
-perform almost any complex task in a database-agnostic, object-oriented
-way.
-
-.. toctree::
- :maxdepth: 2
-
- Overview
- configuration
diff --git a/book/doctrine/dbal/overview.rst b/book/doctrine/dbal/overview.rst
deleted file mode 100644
index b4a88eac22c..00000000000
--- a/book/doctrine/dbal/overview.rst
+++ /dev/null
@@ -1,48 +0,0 @@
-.. index::
- pair: Doctrine; DBAL
-
-DBAL
-====
-
-The `Doctrine`_ Database Abstraction Layer (DBAL) is an abstraction layer that
-sits on top of `PDO`_ and offers an intuitive and flexible API for
-communicating with the most popular relational databases that exist today!
-
-.. tip::
-
- You can read more about the Doctrine DBAL on the official `documentation`_
- website.
-
-To get started you just need to enable and configure the DBAL:
-
-.. code-block:: yaml
-
- # app/config/config.yml
-
- doctrine:
- dbal:
- connections:
- default:
- driver: pdo_mysql
- dbname: Symfony2
- user: root
- password: null
-
-You can then access the Doctrine DBAL connection by accessing the
-``database_connection`` service:
-
-.. code-block:: php
-
- class UserController extends Controller
- {
- public function indexAction()
- {
- $conn = $this->get('database_connection');
-
- $users = $conn->fetchAll('SELECT * FROM users');
- }
- }
-
-.. _PDO: http://www.php.net/pdo
-.. _documentation: http://www.doctrine-project.org/docs/dbal/2.0/en
-.. _Doctrine: http://www.doctrine-project.org
diff --git a/book/doctrine/index.rst b/book/doctrine/index.rst
deleted file mode 100644
index 8890030a120..00000000000
--- a/book/doctrine/index.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-Doctrine
-========
-
-Learn about the Doctrine integration with Symfony2:
-
-.. toctree::
- :maxdepth: 2
-
- model
- orm/index
- dbal/index
- migrations/index
diff --git a/book/doctrine/migrations/index.rst b/book/doctrine/migrations/index.rst
deleted file mode 100644
index 259ebb34c41..00000000000
--- a/book/doctrine/migrations/index.rst
+++ /dev/null
@@ -1,10 +0,0 @@
-Database Migrations
-===================
-
-Learn about Doctrine's database migration tool and start migrating your database
-schema safer and more effectively.
-
-.. toctree::
- :maxdepth: 2
-
- Overview
\ No newline at end of file
diff --git a/book/doctrine/migrations/overview.rst b/book/doctrine/migrations/overview.rst
deleted file mode 100644
index 508485de80c..00000000000
--- a/book/doctrine/migrations/overview.rst
+++ /dev/null
@@ -1,114 +0,0 @@
-.. index::
- pair: Doctrine; Migrations
-
-Migrations
-==========
-
-The database migrations feature is an extension of the database abstraction
-layer and offers you the ability to programmatically deploy new versions of
-your database schema in a safe and standardized way.
-
-.. tip::
-
- You can read more about the Doctrine Database Migrations on the projects
- `documentation`_.
-
-All of the migrations functionality is contained in a few console commands:
-
-.. code-block:: bash
-
- doctrine:migrations
- :diff Generate a migration by comparing your current database to your mapping information.
- :execute Execute a single migration version up or down manually.
- :generate Generate a blank migration class.
- :migrate Execute a migration to a specified version or the latest available version.
- :status View the status of a set of migrations.
- :version Manually add and delete migration versions from the version table.
-
-Every bundle manages its own migrations so when working with the above commands
-you must specify the bundle you want to work with. For example to see the
-status of a bundle migrations you can run the ``status`` command:
-
-.. code-block:: bash
-
- $ php app/console doctrine:migrations:status
-
- == Configuration
-
- >> Name: HelloBundle Migrations
- >> Configuration Source: manually configured
- >> Version Table Name: hello_bundle_migration_versions
- >> Migrations Namespace: Application\Migrations
- >> Migrations Directory: /path/to/symfony-sandbox/app/DoctrineMigrations
- >> Current Version: 0
- >> Latest Version: 0
- >> Executed Migrations: 0
- >> Available Migrations: 0
- >> New Migrations: 0
-
-Now, we can start working with migrations by generating a new blank migration
-class:
-
-.. code-block:: bash
-
- $ php app/console doctrine:migrations:generate
- Generated new migration class to "/path/to/project/app/DoctrineMigrations/Version20100621140655.php"
-
-.. tip::
-
- You may need to create the folder ``/path/to/project/app/DoctrineMigrations``
- before running the ``doctrine:migrations:generate`` command.
-
-Have a look at the newly generated migration class and you will see something
-like the following::
-
- namespace Application\Migrations;
-
- use Doctrine\DBAL\Migrations\AbstractMigration,
- Doctrine\DBAL\Schema\Schema;
-
- class Version20100621140655 extends AbstractMigration
- {
- public function up(Schema $schema)
- {
-
- }
-
- public function down(Schema $schema)
- {
-
- }
- }
-
-If you were to run the ``status`` command it will show that you have one new
-migration to execute:
-
-.. code-block:: bash
-
- $ php app/console doctrine:migrations:status
-
- == Configuration
-
- >> Name: HelloBundle Migrations
- >> Configuration Source: manually configured
- >> Version Table Name: hello_bundle_migration_versions
- >> Migrations Namespace: Application\Migrations
- >> Migrations Directory: /path/to/symfony-sandbox/app/DoctrineMigrations
- >> Current Version: 0
- >> Latest Version: 2010-06-21 14:06:55 (20100621140655)
- >> Executed Migrations: 0
- >> Available Migrations: 1
- >> New Migrations: 1
-
- == Migration Versions
-
- >> 2010-06-21 14:06:55 (20100621140655) not migrated
-
-Now you can add some migration code to the ``up()`` and ``down()`` methods and
-migrate:
-
-.. code-block:: bash
-
- $ php app/console doctrine:migrations:migrate
-
-.. _documentation: http://www.doctrine-project.org/docs/migrations/2.0/en
diff --git a/book/doctrine/model.rst b/book/doctrine/model.rst
deleted file mode 100644
index 989af738afe..00000000000
--- a/book/doctrine/model.rst
+++ /dev/null
@@ -1,234 +0,0 @@
-.. index::
- single: Model
-
-Introduction to the "Model"
-===========================
-
-If you wanted to learn more about fashion models and supermodels, then this
-section won't be helpful to you. But if you're looking to learn about the
-model - the layer in your application that manages data - then keep reading.
-The Model description in this section is the one used when talking about
-*Model-View-Controller* applications.
-
-.. note::
-
- Model-View-Controller (MVC) is an application design pattern
- originally introduced by Trygve Reenskaug for the Smalltalk
- platform. The main idea of MVC is separating presentation from the
- data and separating the controller from presentation. This kind of
- separation lets each part of the application focus on exactly one
- goal. The controller focuses on changing the data of the Model, the Model
- exposes its data for the View, and the View focuses on creating
- representations of the Model (e.g. an HTML page displaying "Blog Posts").
-
-For example, when a user goes to your blog homepage, the user's browser sends
-a request, which is passed to the Controller responsible for rendering
-posts. The Controller calculates which posts should be displayed, retrieves
-``Post`` Models from the database and passes that array to the View. The
-View renders HTML that is interpreted by the browser.
-
-What is a Model anyway?
------------------------
-
-The *Model* is what the "M" in "MVC_" stands for. It is one of the three
-whales of an MVC application. A model is responsible for changing its
-internal state based on requests from the :doc:`controller
-` and giving its current state information
-to the :doc:`view `. It is the main
-application logic container.
-
-For example, if you are building a blog, then you'll have a ``Post``
-model. If you're building a content management system, then you will
-need a ``Page`` model.
-
-.. code-block:: php
-
- title = $title;
- $this->body = $body;
- $this->createdAt = new \DateTime();
- }
-
- public function setTitle($title)
- {
- $this->title = $title;
- $this->updatedAt = new \DateTime();
- }
-
- public function setBody($body)
- {
- $this->body = $body;
- $this->updatedAt = new \DateTime();
- }
-
- public function getTitle()
- {
- return $this->title;
- }
-
- public function getBody()
- {
- return $this->body;
- }
- }
-
-It is obvious that the above class is very simple and testable, yet it's
-mostly complete and will fulfill all the needs of a simple blogging
-engine.
-
-That's it! You now know what a Model in Symfony2 is: it is any class
-that you want to save into some sort of data storage mechanism and
-retrieve later. The rest of the chapter is dedicated to explaining how
-to interact with the database.
-
-Databases and Symfony2
-----------------------
-
-It is worth noting that Symfony2 doesn't come with an object relational
-mapper (ORM) or database abstraction layer (DBAL) of its own; this is
-just not the problem Symfony2 is meant to solve. However, Symfony2 provides
-deep integration with libraries like Doctrine_ and Propel_, which *do*
-provide ORM and DBAL packages, letting you use whichever one you like best.
-
-.. note::
-
- The acronym "ORM" stands for "Object Relational Mapping" and
- represents a programming technique of converting data between
- incompatible type systems. Say we have a ``Post``, which is stored as
- a set of columns in a database, but represented by an instance of
- class ``Post`` in your application. The process of transforming
- the database table into an object is called *object relation mapping*.
- We will also see that this term is slightly outdated, as it is used in
- dealing with relational database management systems. Nowadays there are
- tons of non-relational data storage mechanism available. One such mechanism
- is the *document oriented database* (e.g. MongoDB), which uses a
- new term, "Object Document Mapping" or "ODM".
-
-Going forward, you'll learn about the `Doctrine2 ORM`_ and Doctrine2
-`MongoDB ODM`_ (which serves as an ODM for MongoDB_ - a popular document
-store) as both have the deepest integration with Symfony2 at the time of
-this writing.
-
-A Model is not a Table
-----------------------
-
-The perception of a model class as a database table, where each object
-instance represents a single row, was popularized by the Ruby on Rails
-framework and the `Active Record`_ design pattern. This is a good way of first
-thinking about the model layer of your application, especially if you're
-exposing a simple `CRUD`_ (Create, Retrieve, Update, Delete) interface
-for modifying the data of a model.
-
-But this approach can actually cause problems once you're past the CRUD part
-of your application and start adding more business logic. Here are
-the common limitations of the above-described approach:
-
-* Designing a schema before the actual software that will utilize it is
- like digging a hole before knowing what you need to bury.
- The item might fit in the hole you dig, but what if you're burying a
- large firetruck? This requires an entirely different approach if you want
- to do the job efficiently.
-
-* A database should be tailored to fit your application's needs, not the
- other way around.
-
-* Some data storage engines (like document databases) don't have a notion
- of tables, rows or even a schema, making it hard to use them if your
- perception of a model is that which represents a table.
-
-* Keeping database schema in your head while designing your application
- domain is problematic, and following the rule of the lowest common
- denominator will give you the worst of both worlds.
-
-The `Doctrine2 ORM`_ is designed to remove the need to keep database
-structure in mind and let you concentrate on writing the cleanest
-possible models that will satisfy your business needs. It lets you design
-your classes and their interactions first, before requiring you to even
-think about *how* to persist your data.
-
-Paradigm Shift
---------------
-
-With the introduction of Doctrine2, some of the core paradigms have
-shifted. `Domain Driven Design`_ (DDD) teaches us that objects are best
-modeled when modeled after their real-world prototypes. For example a ``Car``
-object is best modeled to contain ``Engine``, four instances of
-``Tire``, etc. and should be produced by ``CarFactory`` - something that
-knows how to assemble all the parts together. Domain driven design deserves
-a book in its own, as the concept is rather broad. However, for the purposes
-of this chapter, it should be clear that a car cannot start by itself, there
-must be an external impulse to start it. In a similar manner, a model cannot
-save itself without an external impulse, therefore the following piece of
-code violates DDD and will be troublesome to redesign in a clean, testable way.
-
-.. code-block:: php
-
- $post->save();
-
-Hence, Doctrine2 is not your typical `Active Record`_ implementation anymore.
-Instead Doctrine2 uses a different set of patterns, most importantly the
-`Data Mapper`_ and `Unit Of Work`_ patterns. The following example shows
-how to save an entity with Doctrine2:
-
-.. code-block:: php
-
- $manager = //... get object manager instance
-
- $manager->persist($post);
- $manager->flush();
-
-The "object manager" is a central object provided by Doctrine whose job
-is to persist objects. You'll soon learn much more about this service.
-This paradigm shift lets us get rid of any base classes (e.g. ``Post``
-doesn't need to extend a base class) and static dependencies. Any object
-can be saved into a database for later retrieval. More than that, once persisted,
-an object is managed by the object manager until the manager is explicitly
-cleared. This means all object interactions happen in memory,
-without ever going to the database until ``$manager->flush()`` is
-called. Needless to say, this provides an instant database and query
-optimization compared to most other object persistence patterns, as all
-queries are as lazy as possible (i.e. their execution is deferred until the
-latest possible moment).
-
-A very important aspect of the Active Record pattern is performance, or
-rather, the *difficulty* in building a performant system. By using transactions
-and in-memory object change tracking, Doctrine2 minimizes communication
-with the database, saving not only database execution time, but also
-expensive network communication.
-
-Conclusion
-----------
-
-Thanks to Doctrine2, the Model is now probably the simplest concept in
-Symfony2: it is in your complete control and not limited by persistence
-specifics.
-
-By teaming up with Doctrine2 to keep your code relieved of persistence
-details, Symfony2 makes building database-aware applications even
-simpler. Application code stays clean, which will decrease development
-time and improve understandability of the code.
-
-.. _Doctrine: http://www.doctrine-project.org/
-.. _Propel: http://www.propelorm.org/
-.. _Doctrine2 DBAL: http://www.doctrine-project.org/projects/dbal
-.. _Doctrine2 ORM: http://www.doctrine-project.org/projects/orm
-.. _MongoDB ODM: http://www.doctrine-project.org/projects/mongodb_odm
-.. _MongoDB: http://www.mongodb.org
-.. _Domain Driven Design: http://domaindrivendesign.org/
-.. _Active Record: http://martinfowler.com/eaaCatalog/activeRecord.html
-.. _Data Mapper: http://martinfowler.com/eaaCatalog/dataMapper.html
-.. _Unit Of Work: http://martinfowler.com/eaaCatalog/unitOfWork.html
-.. _CRUD: http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
-.. _MVC: http://en.wikipedia.org/wiki/Model-View-Controller
\ No newline at end of file
diff --git a/book/doctrine/orm/configuration.rst b/book/doctrine/orm/configuration.rst
deleted file mode 100644
index b125e18d49d..00000000000
--- a/book/doctrine/orm/configuration.rst
+++ /dev/null
@@ -1,275 +0,0 @@
-.. index::
- single: Configuration; Doctrine ORM
- single: Doctrine; ORM Configuration
-
-Configuration
-=============
-
-In the overview we already described the only necessary configuration option
-"mappings" to get the Doctrine ORM running with Symfony 2. All the other
-configuration options are used with reasonable default values.
-
-This following configuration example shows all the configuration defaults that
-the ORM resolves to:
-
-.. code-block:: yaml
-
- doctrine:
- orm:
- auto_generate_proxy_classes: true
- proxy_namespace: Proxies
- proxy_dir: %kernel.cache_dir%/doctrine/orm/Proxies
- default_entity_manager: default
- entity_managers:
- default:
- mappings:
- HelloBundle: ~
- metadata_cache_driver: array
- query_cache_driver: array
- result_cache_driver: array
-
-There are lots of other configuration options that you can use to overwrite
-certain classes, but those are for very advanced use-cases only. You should
-look at the
-:doc:`configuration reference `
-to get an overview of all the supported options.
-
-.. note::
-
- The ``default_entity_manager`` parameter is mandatory and you have to define
- at least one entity manager. Thus the ``mappings`` configuration is
- mandatory for each entity manager.
-
-For the caching drivers you can specifiy the values "array", "apc", "memcache"
-or "xcache".
-
-The following example shows an overview of the caching configurations:
-
-.. code-block:: yaml
-
- doctrine:
- orm:
- default_entity_manager: default
- entity_managers:
- default:
- mappings:
- HelloBundle: ~
- metadata_cache_driver: apc
- query_cache_driver: xcache
- result_cache_driver:
- type: memcache
- host: localhost
- port: 11211
- instance_class: Memcache
-
-Mapping Configuration
-~~~~~~~~~~~~~~~~~~~~~
-
-Explicit definition of all the mapped entities is the only necessary
-configuration for the ORM and there are several configuration options that you
-can control. The following configuration options exist for a mapping:
-
-- ``type`` One of ``annotations``, ``xml``, ``yml``, ``php`` or ``staticphp``.
- This specifies which type of metadata type your mapping uses.
-- ``dir`` Path to the mapping or entity files (depending on the driver). If
- this path is relative it is assumed to be relative to the bundle root. This
- only works if the name of your mapping is a bundle name. If you want to use
- this option to specifiy absolute paths you should prefix the path with the
- kernel parameters that exist in the DIC (for example %kernel.root_dir%).
-- ``prefix`` A common namespace prefix that all entities of this mapping
- share. This prefix should never conflict with prefixes of other defined
- mappings otherwise some of your entities cannot be found by Doctrine. This
- option defaults to the bundle namespace + ``Entity``, for example for an
- application bundle called ``AcmeHelloBundle`` prefix would be
- ``Acme\HelloBundle\Entity``.
-- ``alias`` Doctrine offers a way to alias entity namespaces to simpler,
- shorter names to be used in DQL queries or for Repository access. When using a
- bundle the alias defaults to the bundle name.
-- ``is_bundle`` This option is a derived value from ``dir`` and by default is
- set to true if dir is relative proved by a ``file_exists()`` check that
- returns false. It is false if the existence check returns true. In this case
- an absolute path was specified and the metadata files are most likely in a
- directory outside of a bundle.
-
-To avoid having to configure lots of information for your mappings you should
-follow these conventions:
-
-1. Put all your entities in a directory ``Entity/`` inside your bundle. For
- example ``Acme/HelloBundle/Entity/``.
-2. If you are using xml, yml or php mapping put all your configuration files
- into the "Resources/config/doctrine/metadata/doctrine/orm/" directory
- suffixed with dcm.xml, dcm.yml or dcm.php respectively.
-3. Annotations is assumed if an ``Entity/`` but no
- "Resources/config/doctrine/metadata/orm/" directory is found.
-
-The following configuration shows a bunch of mapping examples:
-
-.. code-block:: yaml
-
- doctrine:
- orm:
- default_entity_manager: default
- entity_managers:
- default:
- mappings:
- MyBundle1: ~
- MyBundle2: yml
- MyBundle3: { type: annotation, dir: Entity/ }
- MyBundle4: { type: xml, dir: Resources/config/doctrine/mapping }
- MyBundle5:
- type: yml
- dir: my-bundle-mappings-dir
- alias: BundleAlias
- doctrine_extensions:
- type: xml
- dir: %kernel.root_dir%/../src/vendor/DoctrineExtensions/lib/DoctrineExtensions/Entity
- prefix: DoctrineExtensions\Entity\
- alias: DExt
-
-Registering Event Listeners and Subscribers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Doctrine uses the lightweight ``Doctrine\Common\EventManager`` class to trigger
-a number of different events which you can hook into. You can register Event
-Listeners or Subscribers by tagging the respective services with
-``doctrine.dbal._event_listener`` or
-``doctrine.dbal._event_subscriber`` using the Dependency Injection
-container.
-
-You have to use the name of the DBAL connection to clearly identify which
-connection the listeners should be registered with. If you are using multiple
-connections you can hook different events into each connection.
-
-.. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Although the Event Listener and Subscriber tags are prefixed with ``doctrine.dbal``
-these tags also work for the ORM events. Internally Doctrine re-uses the EventManager
-that is registered with the connection for the ORM.
-
-Multiple Entity Managers
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can use multiple EntityManagers in a Symfony application. This is
-necessary if you are using different databases or even vendors with entirely
-different sets of entities.
-
-The following configuration code shows how to define two EntityManagers:
-
-.. code-block:: yaml
-
- doctrine:
- orm:
- default_entity_manager: default
- cache_driver: apc # array, apc, memcache, xcache
- entity_managers:
- default:
- connection: default
- mappings:
- MyBundle1: ~
- MyBundle2: ~
- customer:
- connection: customer
- mappings:
- MyBundle3: ~
-
-Just like the DBAL, if you have configured multiple ``EntityManager``
-instances and want to get a specific one you can use the full service name to
-retrieve it from the Symfony Dependency Injection Container::
-
- class UserController extends Controller
- {
- public function indexAction()
- {
- $em = $this->get('doctrine.orm.entity_manager');
- $defaultEm = $this->get('doctrine.orm.default_entity_manager');
- $customerEm = $this->get('doctrine.orm.customer_entity_manager');
-
- // $em === $defaultEm => true
- // $defaultEm === $customerEm => false
- }
- }
-
-The service "doctrine.orm.entity_manager" is an alias for the default entity
-manager defined in the "default_entity_manager" configuration option.
-
-.. _doctrine-event-config:
-
-Registering Event Listeners and Subscribers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Doctrine ships with an event system that allows to hook into many different
-events happening during the lifecycle of entities or at other occasions.
-
-To register services to act as event listeners or subscribers (listeners from here)
-you have to tag them with the appropriate names. Depending on your use-case you can hook
-a listener into every DBAL Connection and ORM Entity Manager or just into one
-specific DBAL connection and all the EntityManagers that use this connection.
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- doctrine:
- dbal:
- default_connection: default
- connections:
- default:
- driver: pdo_sqlite
- memory: true
-
- services:
- my.listener:
- class: MyEventListener
- tags:
- - { name: doctrine.common.event_listener }
- my.listener2:
- class: MyEventListener2
- tags:
- - { name: doctrine.dbal.default_event_listener }
- my.subscriber:
- class: MyEventSubscriber
- tags:
- - { name: doctrine.dbal.default_event_subscriber }
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/book/doctrine/orm/console.rst b/book/doctrine/orm/console.rst
deleted file mode 100644
index 2e6c2c0af1f..00000000000
--- a/book/doctrine/orm/console.rst
+++ /dev/null
@@ -1,46 +0,0 @@
-.. index::
- single: Doctrine; ORM Console Commands
- single: CLI; Doctrine ORM
-
-Console Commands
-================
-
-The Doctrine2 ORM integration offers several console commands under the
-``doctrine`` namespace. To view the command list you can run the console
-without any arguments or options:
-
-.. code-block:: bash
-
- $ php app/console
- ...
-
- doctrine
- :ensure-production-settings Verify that Doctrine is properly configured for a production environment.
- :schema-tool Processes the schema and either apply it directly on EntityManager or generate the SQL output.
- doctrine:cache
- :clear-metadata Clear all metadata cache for a entity manager.
- :clear-query Clear all query cache for a entity manager.
- :clear-result Clear result cache for a entity manager.
- doctrine:data
- :load Load data fixtures to your database.
- doctrine:database
- :create Create the configured databases.
- :drop Drop the configured databases.
- doctrine:generate
- :entities Generate entity classes and method stubs from your mapping information.
- :entity Generate a new Doctrine entity inside a bundle.
- :proxies Generates proxy classes for entity classes.
- :repositories Generate repository classes from your mapping information.
- doctrine:mapping
- :convert Convert mapping information between supported formats.
- :convert-d1-schema Convert a Doctrine1 schema to Doctrine2 mapping files.
- :import Import mapping information from an existing database.
- doctrine:query
- :dql Executes arbitrary DQL directly from the command line.
- :sql Executes arbitrary SQL directly from the command line.
- doctrine:schema
- :create Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output.
- :drop Processes the schema and either drop the database schema of EntityManager Storage Connection or generate the SQL output.
- :update Processes the schema and either update the database schema of EntityManager Storage Connection or generate the SQL output.
-
- ...
diff --git a/book/doctrine/orm/form.rst b/book/doctrine/orm/form.rst
deleted file mode 100644
index c70a35ac85d..00000000000
--- a/book/doctrine/orm/form.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-Form Integration
-================
-
-There is a tight integration between Doctrine ORM and the Symfony2 Form
-component. Since Doctrine Entities are plain old php objects they nicely
-integrate into the Form component by default, at least for the primitive data
-types such as strings, integers and fields. However you can also integrate
-them nicely with associations.
-
-This is done by the help of a dedicated field:
-:class:`Symfony\\Component\\Form\\EntityChoiceField`. It provides a list of
-choices from which an entity can be selected.
-
-.. code-block:: php
-
- use Symfony\Component\Form\EntityChoiceField;
-
- $field = new EntityChoiceField('users', array(
- 'em' => $em,
- 'class' => 'Acme\\HelloBundle\\Entity\\User',
- 'property' => 'username',
- 'query_builder' => $qb,
- ));
-
- $form->addField($field);
-
-The 'em' option expects the EntityManager, the 'class' option expects the Entity
-class name as an argument. The optional 'property' option allows you to choose
-the property used to display the entity (``__toString`` will be used if not
-set). The optional 'query_builder' option expects a ``QueryBuilder`` instance or
-a closure receiving the repository as an argument and returning the QueryBuilder
-used to get the choices. If not set all entities will be used.
-
-.. tip::
-
- ``EntityChoiceField`` extends :class:`Symfony\\Component\\Form\\ChoiceField`
- so you can also give the array of choices with the 'choices' option instead
- of using a QueryBuilder.
diff --git a/book/doctrine/orm/index.rst b/book/doctrine/orm/index.rst
deleted file mode 100644
index bfc015b9770..00000000000
--- a/book/doctrine/orm/index.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-Object Relational Mapper
-========================
-
-Doctrine's Object Relational Mapper allows for PHP objects to be persisted
-to and retrieved from almost any relational database.
-
-.. toctree::
- :maxdepth: 2
-
- Overview
- Configuration
- Console Commands
- Form
-
- .. code-block:: html+php
-
-
-
-
-
-.. image:: /book/images/forms-simple.png
- :align: center
-
-That's it! By printing ``form_widget(form)``, each field in the form is rendered,
-along with a label and any error messages for each field. As easy as this is,
-it's not very flexible (yet). Later, you'll learn how to customize the form
-output.
-
-Before moving on, notice how the rendered name input field has the value
-of the ``name`` property from the ``$product`` object (i.e. "Test product").
-This is the first job of a form: to take data from an object and translate
-it into a format that's suitable for being rendered in an HTML form.
-
-.. tip::
-
- The form system is smart enough to access the value of the protected ``price``
- property via the ``getPrice()`` and ``setPrice()`` methods on the ``Product``
- class. Unless a property is public, it *must* have a "getter" and "setter"
- method so that the form component can get and put data onto the property.
- For a Boolean property, you can use an "isser" method (e.g. ``isPublished()``)
- instead of a getter (e.g. ``getPublished()``).
-
-Handling Form Submissions
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The second job of a form is to translate user-submitted data back to the
-properties of an object. To make this happen, the submitted data from the
-user must be bound to the form. Add the following functionality to your
-controller:
-
-.. code-block:: php
-
- public function indexAction()
- {
- // ...
-
- $product = new Product();
- $form->setData($product);
-
- $request = $this->get('request');
- if ($request->getMethod() == 'POST') {
- $form->bindRequest($request);
-
- if ($form->isValid()) {
- // perform some action, such as save the object to the database
-
- return $this->redirect($this->generateUrl('store_product_success'));
- }
- }
-
- // ...
- }
-
-Now, when submitting the form, the controller binds the submitted data to
-the form, which translates that data back to the ``name`` and ``price``
-properties of the ``$product`` object. This all happens via the ``bindRequest()``
-method.
-
-This controller follows a common pattern for handling forms, and has three
-possible paths:
-
-#. When initially loading the form in a browser, the request method is ``GET``,
- meaning that the form is simply created and rendered (but not bound);
-
-#. When the user submits the form (i.e. the method is ``POST``), but that
- submitted data is invalid (validation is covered in the next section),
- the form is bound and then rendered, this time displaying all validation
- errors;
-
-#. When the user submits the form with valid data, the form is bound and
- you have the opportunity to perform some action using the ``$product``
- object (e.g. persisting it to the database) before redirecting the user
- to some other page (e.g. a "thank you" or "success" page).
-
-.. note::
-
- Redirecting a user after a successful form submission prevents the user
- from being able to hit "refresh" and re-post the data.
-
-.. index::
- single: Forms; Validation
-
-Form Validation
----------------
-
-In the previous section, you learned how a form can be submitted with valid
-or invalid data. In Symfony2, validation is applied to the underlying object
-(e.g. ``Product``). In other words, the question isn't whether the "form"
-is valid, but rather whether or not the ``$product`` object is valid after
-the form has applied the submitted data to it. Calling ``$form->isValid()``
-is a shortcut that asks the ``$product`` object whether or not it has valid
-data.
-
-Validation is done by adding a set of rules (called constraints) to a class.
-To see this in action, add validation constraints so that the ``name`` field
-cannot be empty and the ``price`` field cannot be empty and must be a non-negative
-number:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # Acme/DemoBundle/Resources/config/validation.yml
- Acme\DemoBundle\Entity\Product:
- properties:
- name:
- - NotBlank: ~
- price:
- - NotBlank: ~
- - Min: 0
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
- 0
-
-
-
-
- .. code-block:: php-annotations
-
- // Acme/StoreBundle/Entity/Product.php
- class Product
- {
- /**
- * @assert:NotBlank()
- */
- public $name;
-
- /**
- * @assert:NotBlank()
- * @assert:Min(0)
- */
- protected $price;
- }
-
- .. code-block:: php
-
- // Acme/StoreBundle/Entity/Product.php
- use Symfony\Component\Validator\Mapping\ClassMetadata;
- use Symfony\Component\Validator\Constraints\NotBlank;
- use Symfony\Component\Validator\Constraints\Min;
-
- class Product
- {
- // ...
-
- public static function loadValidatorMetadata(ClassMetadata $metadata)
- {
- $metadata->addPropertyConstraint('name', new NotBlank());
-
- $metadata->addPropertyConstraint('price', new NotBlank());
- $metadata->addPropertyConstraint('price', new Min(0));
- }
- }
-
-That's it! If you re-submit the form with invalid data, you'll see the corresponding
-errors printed out with the form.
-
-Validation is a very powerful feature of Symfony2 and has its own
-:doc:`dedicated chapter`.
-
-.. index::
- single: Forms; Built-in Field Types
-
-.. _book-forms-type-reference:
-
-Built-in Field Types
---------------------
-
-Symfony comes standard with a large group of field types that cover all of
-the common form fields and data types you'll encounter:
-
-.. include:: /reference/forms/types/map.rst.inc
-
-Of course, you can also create your own custom field types. This topic is
-covered in the ":doc:`/cookbook/forms/create_custom_fields`" article of the
-cookbook.
-
-.. index::
- single: Forms; Field type options
-
-Common Field Type Options
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You may have noticed that the ``price`` field has been passed an array of
-options:
-
-.. code-block:: php
-
- ->add('price', 'money', array('currency' => 'USD'))
-
-Each field type has a number of different options that can be passed to it.
-Many of these are specific to the field type and details can be found in
-the documentation for each type. Some options, however, are shared between
-most fields:
-
-.. include:: /reference/forms/types/options/required.rst.inc
-
-.. include:: /reference/forms/types/options/max_length.rst.inc
-
-.. index::
- single: Forms; Field type guessing
-
-.. _book-forms-field-guessing:
-
-Field Type Guessing
--------------------
-
-Now that you've added validation metadata to the ``Product`` class, Symfony
-already knows a bit about your fields. If you allow it, Symfony can "guess"
-the type of your field and set it up for you. In this example, Symfony will
-guess from the validation that both the ``name`` and ``price`` fields are
-normal ``text`` fields. Since it's right about the ``name`` field, you can
-modify your code so that Symfony guesses the field for you:
-
-.. code-block:: php
-
- public function indexAction()
- {
- $form = $this->get('form.factory')
- ->createBuilder('form', 'product', array(
- 'data_class' => 'Acme\StoreBundle\Entity\Product',
- ))
- ->add('name')
- ->add('price', 'money', array('currency' => 'USD'))
- ->getForm();
- }
-
-You'll notice two differences immediately. First, a ``data_class`` option
-is passed when creating the form. This tells Symfony which class to look
-at when guessing the fields and is required to take advantage of field
-guessing. You can now omit the ``text`` type for the ``name`` field as this
-field is correctly guessed. The ``money`` type was kept, however, for the
-``price`` field as it's more specific than what the system could guess (``text``).
-
-.. note::
-
- The ``createBuilder()`` method takes up to three arguments (but only
- the first is required):
-
- * the string ``form`` (meaning you're building a form);
-
- * a name for the form (e.g. ``product``), which impacts how the ``name``
- attribute of the fields are rendered (e.g. `name="product[name]"`);
-
- * an array of options for the form.
-
-This example is pretty trivial, but field guessing can be a major time saver.
-As you'll see later, adding Doctrine metadata can further improve the system's
-ability to guess field types.
-
-.. index::
- single: Forms; Rendering in a Template
-
-Rendering a Form in a Template
-------------------------------
-
-So far, you've seen how an entire form can be rendered with just one line
-of code. Of course, you'll usually need much more flexibility when rendering:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {# src/Acme/StoreBundle/Resources/views/Default/index.html.twig #}
-
-
-
- .. code-block:: html+php
-
-
-
-
-
-Let's take a look at each part:
-
-* ``form_enctype(form)`` - If at least one field is a file upload field, this
- renders the obligatory ``enctype="multipart/form-data"``;
-
-* ``form_errors(form)`` - This will render any errors global to the whole form
- (field-specific errors are displayed next to each field);
-
-* ``form_row(form.price)`` - Renders the label, any errors, and the HTML
- form widget for the given field (e.g. ``price``);
-
-* ``form_rest(form)`` - This renders any fields that have not yet been rendered
- and is usually a good idea to place at the bottom of each form (in case
- you forgot to output a field or don't want to bother manually rendering
- hidden fields).
-
-The majority of the work is done by the ``form_row`` helper, which renders
-the label, errors and HTML form widget of each field inside a ``div`` tag
-by default. In the :ref:`form-theming` section, you'll learn how
-the ``form_row`` output can be customized on many different levels.
-
-Rendering each Field by Hand
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``form_row`` helper is great because you can very quickly render each
-field of your form (and the markup used for the "row" can be customized as
-well). But since life isn't always so simple, you can also render each field
-entirely by hand:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {{ form_errors(form) }}
-
-
-
- rest($form) ?>
-
-If the auto-generated label for a field isn't quite right, you can specify
-the label manually:
-
-You can also explicitly set the label for a field:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {{ form_label(form.name, 'Product name') }}
-
- .. code-block:: html+php
-
- label($form['name'], 'Product name') ?>
-
-Finally, some field types have additional rendering options that can be passed
-to the widget. These options are documented with each type, but one common
-options is ``attr``, which allows you to modify attributes on the form element.
-The following would add the ``name_field`` class to the rendered input text
-field:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {{ form_widget(form.name, { 'attr': {'class': 'name_field'} }) }}
-
- .. code-block:: html+php
-
- widget($form['name'], array(
- 'attr' => array('class' => 'name_field'),
- )) ?>
-
-.. index::
- single: Forms; Creating form classes
-
-Creating Form Classes
----------------------
-
-As you've seen, a form can be created and used directly in a controller. However,
-a better practice is to build the form in a separate, standalone PHP class,
-which can then be reused anywhere in your application. Create a new class
-that will house the logic for building the product form:
-
-.. code-block:: php
-
- // src/Acme/StoreBundle/Form/ProductType.php
-
- namespace Acme\StoreBundle\Form;
-
- use Symfony\Component\Form\Type\AbstractType;
- use Symfony\Component\Form\FormBuilder;
-
- class ProductType extends AbstractType
- {
- public function buildForm(FormBuilder $builder, array $options)
- {
- $builder->add('name');
- $builder->add('price', 'money', array('currency' => 'USD'));
- }
-
- public function getDefaultOptions(array $options)
- {
- return array(
- 'data_class' => 'Acme\StoreBundle\Entity\Product',
- );
- }
- }
-
-This new class contains all the directions needed to create the product form.
-It can be used to quickly build a form object in the controller:
-
-.. code-block:: php
-
- // src/Acme/StoreBundle/Controller/DefaultController.php
-
- // add this new use statement at the top of the class
- use Acme\StoreBundle\Form\ProductType;
-
- public function indexAction()
- {
- $form = $this->get('form.factory')->create(new ProductType());
-
- // ...
- }
-
-Placing the form logic into its own class means that the class can be easily
-reused and is properly isolated out of the controller. This is the best way
-to create forms, but the choice is ultimately up to you.
-
-.. index::
- single: Forms; Doctrine
-
-Forms and Doctrine
-------------------
-
-The goal of a form is to translate data from an object (e.g. ``Product``)
-to an HTML form and then translate user-submitted data back to the original
-object. As such, the topic of persisting the ``Product`` object to the database
-is entirely unrelated to the topic of forms. If you've configured the ``Product``
-class to be persisted by Doctrine, then persisting it after a form submission
-can be done when the form is valid:
-
-.. code-block:: php
-
- if ($form->isValid()) {
- // persist the $product object to the database
- // or do anything else you need to do
-
- return $this->redirect($this->generateUrl('store_product_success'));
- }
-
-For more information, see the :doc:`Doctrine ORM chapter`.
-
-If the underlying object of a form (e.g. ``Product``) happens to be mapped
-with the Doctrine ORM, the form framework will use that information - along
-with the validation metadata - to guess the type of a particular field.
-
-.. index::
- single: Forms; Embedded forms
-
-Embedded Forms
---------------
-
-Often, you'll want to build a form that will include fields from many different
-objects. For example, a registration form may contain data belonging to
-a ``User`` object as well as many ``Address`` objects. Fortunately, this
-is easy and natural with the form component.
-
-Embedding a Single Object
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Suppose that each ``Product`` belongs to a simple ``Category`` object:
-
-.. code-block:: php
-
- // src/Acme/StoreBundle/Entity/Category.php
- namespace Acme\StoreBundle\Entity;
-
- class Category
- {
- /**
- * @assert:NotBlank()
- */
- public $name;
- }
-
-The ``Product`` class has a new ``$category`` property, indicating to which
-``Category`` it belongs:
-
-.. code-block:: php
-
- class Product
- {
- // ...
-
- /**
- * @assert:Type(type="Acme\StoreBundle\Entity\Category")
- */
- protected $category;
-
- // ...
-
- public function getCategory()
- {
- return $this->category;
- }
-
- public function setCategory(Category $category)
- {
- $this->category = $category;
- }
- }
-
-Now that your application has been updated to reflect the new requirements,
-create a form class so that a ``Category`` object can be modified by the user:
-
-.. code-block:: php
-
- // src/Acme/StoreBundle/Form/CategoryType.php
- namespace Acme\StoreBundle\Form;
-
- use Symfony\Component\Form\Type\AbstractType;
- use Symfony\Component\Form\FormBuilder;
-
- class CategoryType extends AbstractType
- {
- public function buildForm(FormBuilder $builder, array $options)
- {
- $builder->add('name');
- }
-
- public function getDefaultOptions(array $options)
- {
- return array(
- 'data_class' => 'Acme\StoreBundle\Entity\Category',
- );
- }
- }
-
-The type of the ``name`` field is being guessed (as a ``text`` field) from
-the validation metadata of the ``Category`` object.
-
-The end goal is to allow the ``Category`` of a ``Product`` to be modified
-right inside the product form. To accomplish this, add a ``category`` field
-to the ``ProductType`` object whose type is an instance of the new ``CategoryType``
-class:
-
-.. code-block:: php
-
- public function buildForm(FormBuilder $builder, array $options)
- {
- // ...
-
- $builder->add('category', new CategoryType());
- }
-
-The fields from ``CategoryType`` can now be rendered alongside those from
-the ``ProductType`` class. Render the ``Category`` fields in the same way
-as the original ``Product`` fields:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {# ... #}
- {{ form_row(form.price) }}
-
-
-
- rest($form) ?>
-
-
-When the user submits the form, the data submitted for the ``Category`` fields
-is merged onto the ``Category`` object. In other words, everything works
-exactly as it does with the main ``Product`` object. The ``Category`` instance
-is accessible naturally via ``$product->getCategory()`` and can be persisted
-to the database or used however you need.
-
-Embedding a Collection of Forms
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can also embed a collection of forms into one form. This is done by
-using the ``collection`` field type. Assuming that you have a property
-called ``reviews`` and a class called ``ProductReviewType``, you could do
-the following inside ``ProductType``:
-
-.. code-block:: php
-
-
- public function buildForm(FormBuilder $builder, array $options)
- {
- // ...
-
- $builder->add('reviews', 'collection', array(
- 'type' => new ProductReviewType(),
- ));
- }
-
-.. _form-theming:
-
-Form Theming
-------------
-
-Every part of how a form renders can be customized. You're free to change
-how each form "row" renders, change the markup used to render errors, or
-even customize how a textarea tag should be rendered. Nothing is off-limits,
-and different customizations can be used in different places.
-
-Symfony uses templates to render each and every part of a form. In Twig,
-the different pieces of a form - a row, a textarea tag, errors - are represented
-by Twig "blocks". To customize any part of how a form renders, you just need
-to override the appropriate block.
-
-To understand how this works, let's customize the ``form_row`` output and
-add a class attribute to the ``div`` element that surrounds each row. To
-do this, create a new template file that will store the new markup:
-
-.. configuration-block::
-
- .. code-block:: html+jinja
-
- {# src/Acme/StoreBundle/Resources/views/Form/fields.html.twig #}
-
- {% block field_row %}
- {% spaceless %}
-
-
-The ``field_row`` block is the name of the block used when rendering most
-fields via the ``form_row`` function. To use the ``field_row`` block defined
-in this template, add the following to the top of the template that renders
-the form:
-
-.. configuration-block:: php
-
- .. code-block:: html+jinja
-
- {# src/Acme/StoreBundle/Resources/views/Default/index.html.twig #}
- {% form_theme form 'AcmeStoreBundle:Form:fields.html.twig' %}
-
- `.
+
+.. _override-validation:
+
+Validation Metadata
+-------------------
+
+Symfony loads all validation configuration files from every bundle and
+combines them into one validation metadata tree. This means you are able to
+add new constraints to a property, but you cannot override them.
+
+To overcome this, the 3rd party bundle needs to have configuration for
+:doc:`validation groups `. For instance, the FOSUserBundle
+has this configuration. To create your own validation, add the constraints
+to a new validation group:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/validator/validation.yaml
+ FOS\UserBundle\Model\User:
+ properties:
+ plainPassword:
+ - NotBlank:
+ groups: [AcmeValidation]
+ - Length:
+ min: 6
+ minMessage: fos_user.password.short
+ groups: [AcmeValidation]
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Now, update the FOSUserBundle configuration, so it uses your validation groups
+instead of the original ones.
+
+.. _override-translations:
+
+Translations
+------------
+
+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.
+
+For example, to override the translations defined in the
+``translations/AcmeUserBundle.es.yaml`` file of the AcmeUserBundle,
+create a ``/translations/AcmeUserBundle.es.yaml`` file.
+
+.. _`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
new file mode 100644
index 00000000000..e4099d9f81a
--- /dev/null
+++ b/bundles/prepend_extension.rst
@@ -0,0 +1,223 @@
+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 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 ``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
+on another bundle being loaded as well.
+
+To give an Extension the power to do this, it needs to implement
+:class:`Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface`::
+
+ // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php
+ namespace Acme\HelloBundle\DependencyInjection;
+
+ 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): void
+ {
+ // ...
+ }
+ }
+
+Inside the :method:`Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface::prepend`
+method, developers have full access to the :class:`Symfony\\Component\\DependencyInjection\\ContainerBuilder`
+instance just before the :method:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface::load`
+method is called on each of the registered bundle Extensions. In order to
+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 ``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): void
+ {
+ // get all bundles
+ $bundles = $container->getParameter('kernel.bundles');
+ // determine if AcmeGoodbyeBundle is registered
+ if (!isset($bundles['AcmeGoodbyeBundle'])) {
+ // disable AcmeGoodbyeBundle in bundles
+ $config = ['use_acme_goodbye' => false];
+ foreach ($container->getExtensions() as $name => $extension) {
+ 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
+ };
+ }
+ }
+
+ // get the configuration of AcmeHelloExtension (it's a list of configuration)
+ $configs = $container->getExtensionConfig($this->getAlias());
+
+ // 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
+``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
+
+ # config/packages/acme_something.yaml
+ acme_something:
+ # ...
+ use_acme_goodbye: false
+ entity_manager_name: non_default
+
+ acme_other:
+ # ...
+ use_acme_goodbye: false
+
+ .. code-block:: xml
+
+
+
+
+
+
+ non_default
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // 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
+ {
+ // ...
+
+ $containerConfigurator->extension('framework', [
+ 'cache' => ['prefix_seed' => 'foo/bar'],
+ ], prepend: true);
+
+ // ...
+ }
+ }
+
+.. 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
+----------------------------------------------------
+
+If there is more than one bundle that prepends the same extension and defines
+the same key, the bundle that is registered **first** will take priority:
+next bundles won't override this specific config setting.
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/components/asset.rst b/components/asset.rst
new file mode 100644
index 00000000000..d6d3f485859
--- /dev/null
+++ b/components/asset.rst
@@ -0,0 +1,432 @@
+The Asset Component
+===================
+
+ The Asset component manages URL generation and versioning of web assets such
+ as CSS stylesheets, JavaScript files and image files.
+
+In the past, it was common for web applications to hard-code the URLs of web assets.
+For example:
+
+.. code-block:: html
+
+
+
+
+
+
+
+This practice is no longer recommended unless the web application is extremely
+simple. Hardcoding URLs can be a disadvantage because:
+
+* **Templates get verbose**: you have to write the full path for each
+ asset. When using the Asset component, you can group assets in packages to
+ avoid repeating the common part of their path;
+* **Versioning is difficult**: it has to be custom managed for each
+ application. Adding a version (e.g. ``main.css?v=5``) to the asset URLs
+ is essential for some applications because it allows you to control how
+ the assets are cached. The Asset component allows you to define different
+ versioning strategies for each package;
+* **Moving assets' location** is cumbersome and error-prone: it requires you to
+ carefully update the URLs of all assets included in all templates. The Asset
+ component allows to move assets effortlessly just by changing the base path
+ value associated with the package of assets;
+* **It's nearly impossible to use multiple CDNs**: this technique requires
+ you to change the URL of the asset randomly for each request. The Asset component
+ provides out-of-the-box support for any number of multiple CDNs, both regular
+ (``http://``) and secure (``https://``).
+
+Installation
+------------
+
+.. code-block:: terminal
+
+ $ composer require symfony/asset
+
+.. include:: /components/require_autoload.rst.inc
+
+Usage
+-----
+
+.. _asset-packages:
+
+Asset Packages
+~~~~~~~~~~~~~~
+
+The Asset component manages assets through packages. A package groups all the
+assets which share the same properties: versioning strategy, base path, CDN hosts,
+etc. In the following basic example, a package is created to manage assets without
+any versioning::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;
+
+ $package = new Package(new EmptyVersionStrategy());
+
+ // Absolute path
+ echo $package->getUrl('/image.png');
+ // result: /image.png
+
+ // Relative path
+ echo $package->getUrl('image.png');
+ // result: image.png
+
+Packages implement :class:`Symfony\\Component\\Asset\\PackageInterface`,
+which defines the following two methods:
+
+:method:`Symfony\\Component\\Asset\\PackageInterface::getVersion`
+ Returns the asset version for an asset.
+
+:method:`Symfony\\Component\\Asset\\PackageInterface::getUrl`
+ Returns an absolute or root-relative public path.
+
+With a package, you can:
+
+A) :ref:`version the assets `;
+B) set a :ref:`common base path ` (e.g. ``/css``)
+ for the assets;
+C) :ref:`configure a CDN ` for the assets
+
+.. _component-assets-versioning:
+
+Versioned Assets
+~~~~~~~~~~~~~~~~
+
+One of the main features of the Asset component is the ability to manage
+the versioning of the application's assets. Asset versions are commonly used
+to control how these assets are cached.
+
+Instead of relying on a simple version mechanism, the Asset component allows
+you to define advanced versioning strategies via PHP classes. The two built-in
+strategies are the :class:`Symfony\\Component\\Asset\\VersionStrategy\\EmptyVersionStrategy`,
+which doesn't add any version to the asset and :class:`Symfony\\Component\\Asset\\VersionStrategy\\StaticVersionStrategy`,
+which allows you to set the version with a format string.
+
+In this example, the ``StaticVersionStrategy`` is used to append the ``v1``
+suffix to any asset path::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy;
+
+ $package = new Package(new StaticVersionStrategy('v1'));
+
+ // Absolute path
+ echo $package->getUrl('/image.png');
+ // result: /image.png?v1
+
+ // Relative path
+ echo $package->getUrl('image.png');
+ // result: image.png?v1
+
+In case you want to modify the version format, pass a ``sprintf``-compatible
+format string as the second argument of the ``StaticVersionStrategy``
+constructor::
+
+ // puts the 'version' word before the version value
+ $package = new Package(new StaticVersionStrategy('v1', '%s?version=%s'));
+
+ echo $package->getUrl('/image.png');
+ // result: /image.png?version=v1
+
+ // puts the asset version before its path
+ $package = new Package(new StaticVersionStrategy('v1', '%2$s/%1$s'));
+
+ echo $package->getUrl('/image.png');
+ // result: /v1/image.png
+
+ echo $package->getUrl('image.png');
+ // result: v1/image.png
+
+JSON File Manifest
+..................
+
+A popular strategy to manage asset versioning, which is used by tools such as
+`Webpack`_, is to generate a JSON file mapping all source file names to their
+corresponding output file:
+
+.. code-block:: json
+
+ {
+ "css/app.css": "build/css/app.b916426ea1d10021f3f17ce8031f93c2.css",
+ "js/app.js": "build/js/app.13630905267b809161e71d0f8a0c017b.js",
+ "...": "..."
+ }
+
+In those cases, use the
+:class:`Symfony\\Component\\Asset\\VersionStrategy\\JsonManifestVersionStrategy`::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
+
+ // assumes the JSON file above is called "rev-manifest.json"
+ $package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json'));
+
+ echo $package->getUrl('css/app.css');
+ // result: build/css/app.b916426ea1d10021f3f17ce8031f93c2.css
+
+If you request an asset that is *not found* in the ``rev-manifest.json`` file,
+the original - *unmodified* - asset path will be returned. The ``$strictMode``
+argument helps debug issues because it throws an exception when the asset is not
+listed in the manifest::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
+
+ // The value of $strictMode can be specific per environment "true" for debugging and "false" for stability.
+ $strictMode = true;
+ // assumes the JSON file above is called "rev-manifest.json"
+ $package = new Package(new JsonManifestVersionStrategy(__DIR__.'/rev-manifest.json', null, $strictMode));
+
+ echo $package->getUrl('not-found.css');
+ // error:
+
+If your JSON file is not on your local filesystem but is accessible over HTTP,
+use the :class:`Symfony\\Component\\Asset\\VersionStrategy\\JsonManifestVersionStrategy`
+with the :doc:`HttpClient component `::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy;
+ use Symfony\Component\HttpClient\HttpClient;
+
+ $httpClient = HttpClient::create();
+ $manifestUrl = 'https://cdn.example.com/rev-manifest.json';
+ $package = new Package(new JsonManifestVersionStrategy($manifestUrl, $httpClient));
+
+Custom Version Strategies
+.........................
+
+Use the :class:`Symfony\\Component\\Asset\\VersionStrategy\\VersionStrategyInterface`
+to define your own versioning strategy. For example, your application may need
+to append the current date to all its web assets in order to bust the cache
+every day::
+
+ use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
+
+ class DateVersionStrategy implements VersionStrategyInterface
+ {
+ private string $version;
+
+ public function __construct()
+ {
+ $this->version = date('Ymd');
+ }
+
+ public function getVersion(string $path): string
+ {
+ return $this->version;
+ }
+
+ public function applyVersion(string $path): string
+ {
+ return sprintf('%s?v=%s', $path, $this->getVersion($path));
+ }
+ }
+
+.. _component-assets-path-package:
+
+Grouped Assets
+~~~~~~~~~~~~~~
+
+Often, many assets live under a common path (e.g. ``/static/images``). If
+that's your case, replace the default :class:`Symfony\\Component\\Asset\\Package`
+class with :class:`Symfony\\Component\\Asset\\PathPackage` to avoid repeating
+that path over and over again::
+
+ use Symfony\Component\Asset\PathPackage;
+ // ...
+
+ $pathPackage = new PathPackage('/static/images', new StaticVersionStrategy('v1'));
+
+ echo $pathPackage->getUrl('logo.png');
+ // result: /static/images/logo.png?v1
+
+ // Base path is ignored when using absolute paths
+ echo $pathPackage->getUrl('/logo.png');
+ // result: /logo.png?v1
+
+Request Context Aware Assets
+............................
+
+If you are also using the :doc:`HttpFoundation `
+component in your project (for instance, in a Symfony application), the ``PathPackage``
+class can take into account the context of the current request::
+
+ use Symfony\Component\Asset\Context\RequestStackContext;
+ use Symfony\Component\Asset\PathPackage;
+ // ...
+
+ $pathPackage = new PathPackage(
+ '/static/images',
+ new StaticVersionStrategy('v1'),
+ new RequestStackContext($requestStack)
+ );
+
+ echo $pathPackage->getUrl('logo.png');
+ // result: /somewhere/static/images/logo.png?v1
+
+ // Both "base path" and "base url" are ignored when using absolute path for asset
+ echo $pathPackage->getUrl('/logo.png');
+ // result: /logo.png?v1
+
+Now that the request context is set, the ``PathPackage`` will prepend the
+current request base URL. So, for example, if your entire site is hosted under
+the ``/somewhere`` directory of your web server root directory and the configured
+base path is ``/static/images``, all paths will be prefixed with
+``/somewhere/static/images``.
+
+.. _component-assets-cdn:
+
+Absolute Assets and CDNs
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Applications that host their assets on different domains and CDNs (*Content
+Delivery Networks*) should use the :class:`Symfony\\Component\\Asset\\UrlPackage`
+class to generate absolute URLs for their assets::
+
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $urlPackage = new UrlPackage(
+ 'https://static.example.com/images/',
+ new StaticVersionStrategy('v1')
+ );
+
+ echo $urlPackage->getUrl('/logo.png');
+ // result: https://static.example.com/images/logo.png?v1
+
+You can also pass a schema-agnostic URL::
+
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $urlPackage = new UrlPackage(
+ '//static.example.com/images/',
+ new StaticVersionStrategy('v1')
+ );
+
+ echo $urlPackage->getUrl('/logo.png');
+ // result: //static.example.com/images/logo.png?v1
+
+This is useful because assets will automatically be requested via HTTPS if
+a visitor is viewing your site in https. If you want to use this, make sure
+that your CDN host supports HTTPS.
+
+In case you serve assets from more than one domain to improve application
+performance, pass an array of URLs as the first argument to the ``UrlPackage``
+constructor::
+
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $urls = [
+ 'https://static1.example.com/images/',
+ 'https://static2.example.com/images/',
+ ];
+ $urlPackage = new UrlPackage($urls, new StaticVersionStrategy('v1'));
+
+ echo $urlPackage->getUrl('/logo.png');
+ // result: https://static1.example.com/images/logo.png?v1
+ echo $urlPackage->getUrl('/icon.png');
+ // result: https://static2.example.com/images/icon.png?v1
+
+For each asset, one of the URLs will be randomly used. But, the selection
+is deterministic, meaning that each asset will always be served by the same
+domain. This behavior simplifies the management of HTTP cache.
+
+Request Context Aware Assets
+............................
+
+Similarly to application-relative assets, absolute assets can also take into
+account the context of the current request. In this case, only the request
+scheme is considered, in order to select the appropriate base URL (HTTPs or
+protocol-relative URLs for HTTPs requests, any base URL for HTTP requests)::
+
+ use Symfony\Component\Asset\Context\RequestStackContext;
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $urlPackage = new UrlPackage(
+ ['http://example.com/', 'https://example.com/'],
+ new StaticVersionStrategy('v1'),
+ new RequestStackContext($requestStack)
+ );
+
+ echo $urlPackage->getUrl('/logo.png');
+ // assuming the RequestStackContext says that we are on a secure host
+ // result: https://example.com/logo.png?v1
+
+Named Packages
+~~~~~~~~~~~~~~
+
+Applications that manage lots of different assets may need to group them in
+packages with the same versioning strategy and base path. The Asset component
+includes a :class:`Symfony\\Component\\Asset\\Packages` class to simplify
+management of several packages.
+
+In the following example, all packages use the same versioning strategy, but
+they all have different base paths::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\Packages;
+ use Symfony\Component\Asset\PathPackage;
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $versionStrategy = new StaticVersionStrategy('v1');
+
+ $defaultPackage = new Package($versionStrategy);
+
+ $namedPackages = [
+ 'img' => new UrlPackage('https://img.example.com/', $versionStrategy),
+ 'doc' => new PathPackage('/somewhere/deep/for/documents', $versionStrategy),
+ ];
+
+ $packages = new Packages($defaultPackage, $namedPackages);
+
+The ``Packages`` class allows to define a default package, which will be applied
+to assets that don't define the name of the package to use. In addition, this
+application defines a package named ``img`` to serve images from an external
+domain and a ``doc`` package to avoid repeating long paths when linking to a
+document inside a template::
+
+ echo $packages->getUrl('/main.css');
+ // result: /main.css?v1
+
+ echo $packages->getUrl('/logo.png', 'img');
+ // result: https://img.example.com/logo.png?v1
+
+ echo $packages->getUrl('resume.pdf', 'doc');
+ // result: /somewhere/deep/for/documents/resume.pdf?v1
+
+Local Files and Other Protocols
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In addition to HTTP this component supports other protocols (such as ``file://``
+and ``ftp://``). This allows for example to serve local files in order to
+improve performance::
+
+ use Symfony\Component\Asset\UrlPackage;
+ // ...
+
+ $localPackage = new UrlPackage(
+ 'file:///path/to/images/',
+ new EmptyVersionStrategy()
+ );
+
+ $ftpPackage = new UrlPackage(
+ 'ftp://example.com/images/',
+ new EmptyVersionStrategy()
+ );
+
+ echo $localPackage->getUrl('/logo.png');
+ // result: file:///path/to/images/logo.png
+
+ echo $ftpPackage->getUrl('/logo.png');
+ // result: ftp://example.com/images/logo.png
+
+Learn more
+----------
+
+* :doc:`How to manage CSS and JavaScript assets in Symfony applications `
+* :doc:`WebLink component ` to preload assets using HTTP/2.
+
+.. _`Webpack`: https://webpack.js.org/
diff --git a/components/browser_kit.rst b/components/browser_kit.rst
new file mode 100644
index 00000000000..8cf0772298c
--- /dev/null
+++ b/components/browser_kit.rst
@@ -0,0 +1,409 @@
+The BrowserKit Component
+========================
+
+ The BrowserKit component simulates the behavior of a web browser, allowing
+ you to make requests, click on links and submit forms programmatically.
+
+Installation
+------------
+
+.. code-block:: terminal
+
+ $ composer require symfony/browser-kit
+
+.. include:: /components/require_autoload.rst.inc
+
+Basic Usage
+-----------
+
+.. seealso::
+
+ This article explains how to use the BrowserKit features as an independent
+ component in any PHP application. Read the :ref:`Symfony Functional Tests `
+ article to learn about how to use it in Symfony applications.
+
+Creating a Client
+~~~~~~~~~~~~~~~~~
+
+The component only provides an abstract client and does not provide any backend
+ready to use for the HTTP layer. To create your own client, you must extend the
+``AbstractBrowser`` class and implement the
+:method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::doRequest` method.
+This method accepts a request and should return a response::
+
+ namespace Acme;
+
+ use Symfony\Component\BrowserKit\AbstractBrowser;
+ use Symfony\Component\BrowserKit\Response;
+
+ class Client extends AbstractBrowser
+ {
+ protected function doRequest($request): Response
+ {
+ // ... convert request into a response
+
+ return new Response($content, $status, $headers);
+ }
+ }
+
+For a simple implementation of a browser based on the HTTP layer, have a look
+at the :class:`Symfony\\Component\\BrowserKit\\HttpBrowser` provided by
+:ref:`this component `. For an implementation based
+on ``HttpKernelInterface``, have a look at the :class:`Symfony\\Component\\HttpKernel\\HttpClientKernel`
+provided by the :doc:`HttpKernel component `.
+
+Making Requests
+~~~~~~~~~~~~~~~
+
+Use the :method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::request` method to
+make HTTP requests. The first two arguments are the HTTP method and the requested
+URL::
+
+ use Acme\Client;
+
+ $client = new Client();
+ $crawler = $client->request('GET', '/');
+
+The value returned by the ``request()`` method is an instance of the
+:class:`Symfony\\Component\\DomCrawler\\Crawler` class, provided by the
+:doc:`DomCrawler component `, which allows accessing
+and traversing HTML elements programmatically.
+
+The :method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::jsonRequest` method,
+which defines the same arguments as the ``request()`` method, is a shortcut to
+convert the request parameters into a JSON string and set the needed HTTP headers::
+
+ use Acme\Client;
+
+ $client = new Client();
+ // this encodes parameters as JSON and sets the required CONTENT_TYPE and HTTP_ACCEPT headers
+ $crawler = $client->jsonRequest('GET', '/', ['some_parameter' => 'some_value']);
+
+The :method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::xmlHttpRequest` method,
+which defines the same arguments as the ``request()`` method, is a shortcut to
+make AJAX requests::
+
+ use Acme\Client;
+
+ $client = new Client();
+ // the required HTTP_X_REQUESTED_WITH header is added automatically
+ $crawler = $client->xmlHttpRequest('GET', '/');
+
+Clicking Links
+~~~~~~~~~~~~~~
+
+The ``AbstractBrowser`` is capable of simulating link clicks. Pass the text
+content of the link and the client will perform the needed HTTP GET request to
+simulate the link click::
+
+ use Acme\Client;
+
+ $client = new Client();
+ $client->request('GET', '/product/123');
+
+ $crawler = $client->clickLink('Go elsewhere...');
+
+If you need the :class:`Symfony\\Component\\DomCrawler\\Link` object that
+provides access to the link properties (e.g. ``$link->getMethod()``,
+``$link->getUri()``), use this other method::
+
+ // ...
+ $crawler = $client->request('GET', '/product/123');
+ $link = $crawler->selectLink('Go elsewhere...')->link();
+ $client->click($link);
+
+The :method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::click` and
+:method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::clickLink` methods
+can take an optional ``serverParameters`` argument. This
+parameter allows to send additional information like headers when clicking
+on a link::
+
+ use Acme\Client;
+
+ $client = new Client();
+ $client->request('GET', '/product/123');
+
+ // works both with `click()`...
+ $link = $crawler->selectLink('Go elsewhere...')->link();
+ $client->click($link, ['X-Custom-Header' => 'Some data']);
+
+ // ... and `clickLink()`
+ $crawler = $client->clickLink('Go elsewhere...', ['X-Custom-Header' => 'Some data']);
+
+Submitting Forms
+~~~~~~~~~~~~~~~~
+
+The ``AbstractBrowser`` is also capable of submitting forms. First, select the
+form using any of its buttons and then override any of its properties (method,
+field values, etc.) before submitting it::
+
+ use Acme\Client;
+
+ $client = new Client();
+ $crawler = $client->request('GET', 'https://github.com/login');
+
+ // find the form with the 'Log in' button and submit it
+ // 'Log in' can be the text content, id or name of a